62-362 Fall 2020 https://courses.ideate.cmu.edu/62-362/f2020 Electronic Logics && Creative Practice Tue, 29 Dec 2020 23:10:19 +0000 en-US hourly 1 https://wordpress.org/?v=5.4.15 Initial Ideation: Arrows John https://courses.ideate.cmu.edu/62-362/f2020/initial-ideation-arrows-john/ Sat, 19 Dec 2020 00:42:35 +0000 https://courses.ideate.cmu.edu/62-362/f2020/?p=10737 These are sketches and plans of some of the ideas I was planning for the third project.

I was considering a tombstone music box similar to the jewelry boxes with spinning toys inside.

 

I also was thinking of an event simulator where placing dolls on a board would place them in a simulation in unity. I also wanted to use a joystick to create a player controller for a model I animated with maya.

]]>
“Letters” by The Wind https://courses.ideate.cmu.edu/62-362/f2020/letters-by-the-wind/ Fri, 18 Dec 2020 22:25:14 +0000 https://courses.ideate.cmu.edu/62-362/f2020/?p=10720 Sorry, but you do not have permission to view this content. ]]> Sorry, but you do not have permission to view this content. ]]> Barber Shop https://courses.ideate.cmu.edu/62-362/f2020/gates-documentation-john/ Fri, 18 Dec 2020 21:58:56 +0000 https://courses.ideate.cmu.edu/62-362/f2020/?p=10734 I decided to make an automaton music box for this assignment. I never expected it could be so much work. Of all the projects this semester, I think I learned the most doing this one. In the process I accidentally fried 2 arduinos and a good handful of other electronics. While it was frustrating at the time, the other projects were much easier thanks to the knowledge I gained.

My friend Jackson made the 3 songs used after I gave him some small prompts, I was happy to get to include a piece of him in this odd little project.

The initial idea was a barber shop that caught on fire, not sure how well that was reflected but I was happy with the mood of the finished piece.

 

Electronics and Structure:

Some of the fabrication:

Some early sketches and plans:

 

Electronics:

arduino uno, 7408 quad AND gate, 7404 NOT gate, L293D motor driver, DFPlayer mini, 2 hobby gear motors, 1 continuous rotation servo, micro sd card, micro sd card reader/writer, 3 pushbuttons, 2 potentiometers, speakers, neo pixel ring, 4 yellow LEDs, 8 red LEDs, wires, resistor, breadboard, 5v power source

Materials:

wood, cardstock, cereal boxes, sewing pins, paperclips, wire, plastic bags, old clothing/fabric, thread, tissues, electrical tape, gaffer tape, duct tape, staples, boba straw, pebbles, Styrofoam

Tools:

soldering iron and fan, scissors, wire cutters, needle, pliers, precision knife, compass (drawing tool), stapler, pencils/pens, ruler, cutting mat

 

Code:

Everything but music:

#include "Arduino.h"
#include "SoftwareSerial.h"
#include "DFRobotDFPlayerMini.h"

#include <Adafruit_NeoPixel.h>
#include <Servo.h>

#define PIN            9
#define NUMPIXELS      12

SoftwareSerial mySoftwareSerial(10, 11); // RX, TX
DFRobotDFPlayerMini myDFPlayer;
void printDetail(uint8_t type, int value);

bool stage1 = false;
bool stage2 = false;
bool stage3 = false;

bool button1 = false;
bool button2 = false;
bool button3 = false;

bool song1 = false;
bool song2 = false;
bool song3 = false;

int value;
int angle;

Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

uint32_t red = pixels.Color(250,0,0);

Servo myservo;

void setup() {
  pixels.begin(); //backlight 
  pixels.setBrightness(0); 
  pixels.show();         

  pinMode(5, OUTPUT); //m1
  pinMode(6, OUTPUT); //m2
  myservo.attach(3); //m3
  
  pinMode(7, INPUT); //button1
  pinMode(8, INPUT); //button2
  pinMode(4, INPUT); //button3
  
  pinMode(A0, INPUT); //p1
  pinMode(A1, INPUT); //p2

  mySoftwareSerial.begin(9600);
  Serial.begin(115200);

  Serial.println();
  Serial.println(F("DFRobot DFPlayer Mini Demo"));
  Serial.println(F("Initializing DFPlayer ... (May take 3~5 seconds)"));

  if (!myDFPlayer.begin(mySoftwareSerial)) {  //Use softwareSerial to communicate with mp3.
    Serial.println(F("Unable to begin:"));
    Serial.println(F("1.Please recheck the connection!"));
    Serial.println(F("2.Please insert the SD card!"));
    while(true);
  }
  Serial.println(F("DFPlayer Mini online."));

  myDFPlayer.volume(30);  //Set volume value. From 0 to 30
}

void loop() {

  //part 1
  if (digitalRead(7) == HIGH) {
    analogWrite(5, analogRead(A0)/4);
    button1 = true; 
  } else {
    button1 = false;
  }

  //part 2
  if (digitalRead(8) == HIGH) {
    analogWrite(6, analogRead(A0)/4);
    button2 = true;
  } else {
    button2 = false;
  }

  //part 3
  if (digitalRead(4) == HIGH) {
    //m3
    value = analogRead(A1);
    angle = map(value, 0, 1023, 10, 170); 
    myservo.write(angle); 
    //backlight
    pixels.setBrightness(150);
    pixels.fill(red);
    pixels.show();
    button3 = true;
  } else {
    myservo.write(10);
    pixels.setBrightness(0);
    pixels.show();
    button3 = false;
  }

  //dfmini 
  stage1 = (button1 && !button2);
  stage2 = (button1 && button2 && !button3);
  stage3 = (button1 && button2 && button3);
  
  if (stage1 && !stage2 && !stage3 && !song1) {
    myDFPlayer.play(3);
    song1 = true;
    song2 = false;
    song3 = false;
  } else if (stage2 && !stage3 && !song2){
    myDFPlayer.play(1);
    song2 = true;
    song1 = false;
    song3 = false;
  } else if (stage3 && !song3){
    myDFPlayer.play(2);
    song3 = true;
    song1 = false;
    song2 = false;
  } 
  
  if (!stage1 && !stage2 && !stage3) {
    myDFPlayer.stop();
    song1 = false;
    song2 = false;
    song3 = false;
  }
  
}

 

dfmini controls:

/***************************************************
DFPlayer - A Mini MP3 Player For Arduino
 <https://www.dfrobot.com/index.php?route=product/product&product_id=1121>

 ***************************************************
 This example shows the basic function of library for DFPlayer.

 Created 2016-12-07
 By [Angelo qiao](Angelo.qiao@dfrobot.com)

 GNU Lesser General Public License.
 See <http://www.gnu.org/licenses/> for details.
 All above must be included in any redistribution
 ****************************************************/

/***********Notice and Trouble shooting***************
 1.Connection and Diagram can be found here
 <https://www.dfrobot.com/wiki/index.php/DFPlayer_Mini_SKU:DFR0299#Connection_Diagram>
 2.This code is tested on Arduino Uno, Leonardo, Mega boards.
 ****************************************************/

#include "Arduino.h"
#include "SoftwareSerial.h"
#include "DFRobotDFPlayerMini.h"

SoftwareSerial mySoftwareSerial(10, 11); // RX, TX
DFRobotDFPlayerMini myDFPlayer;
void printDetail(uint8_t type, int value);

int counter = 1;

void setup()
{
  pinMode(7, INPUT);
  
  mySoftwareSerial.begin(9600);
  Serial.begin(115200);

  Serial.println();
  Serial.println(F("DFRobot DFPlayer Mini Demo"));
  Serial.println(F("Initializing DFPlayer ... (May take 3~5 seconds)"));

  if (!myDFPlayer.begin(mySoftwareSerial)) {  //Use softwareSerial to communicate with mp3.
    Serial.println(F("Unable to begin:"));
    Serial.println(F("1.Please recheck the connection!"));
    Serial.println(F("2.Please insert the SD card!"));
    while(true);
  }
  Serial.println(F("DFPlayer Mini online."));

  myDFPlayer.volume(30);  //Set volume value. From 0 to 30
}

void loop()
{
  
  if (digitalRead(7) == HIGH) {
    if (counter == 1) {
      myDFPlayer.play(3);
    } else if (counter == 2) {
      myDFPlayer.play(1);
    } else if (counter == 3) {
      myDFPlayer.play(2);
    } else {
      myDFPlayer.stop();
    }
    counter = counter + 1;
    if (counter == 5) {
      counter = 1;
    }
    delay(200);
  }

  if (myDFPlayer.available()) {
    printDetail(myDFPlayer.readType(), myDFPlayer.read()); //Print the detail message from DFPlayer to handle different errors and states.
  }
}

void printDetail(uint8_t type, int value){
  switch (type) {
    case TimeOut:
      Serial.println(F("Time Out!"));
      break;
    case WrongStack:
      Serial.println(F("Stack Wrong!"));
      break;
    case DFPlayerCardInserted:
      Serial.println(F("Card Inserted!"));
      break;
    case DFPlayerCardRemoved:
      Serial.println(F("Card Removed!"));
      break;
    case DFPlayerCardOnline:
      Serial.println(F("Card Online!"));
      break;
    case DFPlayerPlayFinished:
      Serial.print(F("Number:"));
      Serial.print(value);
      Serial.println(F(" Play Finished!"));
      break;
    case DFPlayerError:
      Serial.print(F("DFPlayerError:"));
      switch (value) {
        case Busy:
          Serial.println(F("Card not found"));
          break;
        case Sleeping:
          Serial.println(F("Sleeping"));
          break;
        case SerialWrongStack:
          Serial.println(F("Get Wrong Stack"));
          break;
        case CheckSumNotMatch:
          Serial.println(F("Check Sum Not Match"));
          break;
        case FileIndexOut:
          Serial.println(F("File Index Out of Bound"));
          break;
        case FileMismatch:
          Serial.println(F("Cannot Find File"));
          break;
        case Advertise:
          Serial.println(F("In Advertise"));
          break;
        default:
          break;
      }
      break;
    default:
      break;
  }
}

 

Useful references:

DFPlayermini

https://wiki.dfrobot.com/DFPlayer_Mini_SKU_DFR0299

https://www.instructables.com/Tutorial-of-MP3-TF-16P/

L293d

https://www.tinkercad.com/things/gigwXYoyaDb

Neopixel_ring

https://www.instructables.com/3D-Printed-NeoPixel-Ring-Butterfly-With-Tinkercad/

https://www.tinkercad.com/things/c4nyZ9UpWer

 

Libraries found on Github:

https://github.com/adafruit/Adafruit_NeoPixel

https://github.com/DFRobot/DFRobotDFPlayerMini

 

TinkerCad Circuits I used to troubleshoot:

https://www.tinkercad.com/things/0evKMIHM0Ai-fantabulous-inari/editel?sharecode=NX5CYihf8vkyRuLa-rzRwghpmeox1-OVk11WzY99hrU

https://www.tinkercad.com/things/7uIwm5bLp6N-copy-of-project-1/editel?sharecode=YA4F9Lz2rxujlDz2OgQx0Xv3PKQZNSbunOs2S8PjWWM

]]>
Dance Space https://courses.ideate.cmu.edu/62-362/f2020/gates-dance-space/ Fri, 18 Dec 2020 17:46:36 +0000 https://courses.ideate.cmu.edu/62-362/f2020/?p=10694

Project Statement:
In order to capture and enhance my existing rountine of dancing in my living room, this dance space has been created. It is meant to be used as a place to let go of any stress, it may be added to a daily routine- or not. As you enjoy tea and dance, the space around you reacts to your actions. The idea is to move freely and unpredictably through the living space until the knit yarn pieces are lifted. I plan to complete this project with a final piece of documentation which will be a video of me dancing to music.

How to Use:
In order to turn the light on, the tea kettle must be lifted. in order to make the “worms” move up, three or more buttons one the ground must be pressed. Enjoy the space, move freely, and dance!

 

Process and Issues:

I ran into some issues while constructing the pieces. I didn’t predict well enough, how many things I would need into order to complete the project successfully. I think, for the time we were given I may have created a project that was a bit too big/complex in the manufacturing process. Another issue I ran into was not prdicting how long the clay was going to take to dry. Once dry, it will be spray painted the same shade as the chair.

The electronics/programming portion of the project ran pretty smoothly except for the uploading the program into the arduino. I didn’t have the library I needed for the code to run, so I downloaded the right library for the motor.
Looking back, I am happy with the direction this project has gone. If I were to change anything, it would most likely be the construction of my pulley system. Although it works, it is very fragile and I would have liked it to be more rigid so that there would be no risk in the whole system falling apart. I’m satisfied with the interactivity and tangibility of this project. I feel that I have achieved a very intuitive system as to how to operate the piece. Perhaps I could have have used less fragile floor pieces, however I enjoy how delicate they are- even the possibility of the clay cracking as I step on it.

 

Clay Floor Pieces

 

 

Electronics Setup

 

Planning

]]>
Gateway into the Machine https://courses.ideate.cmu.edu/62-362/f2020/project-3-gateway-into-the-machine-documentation/ Wed, 16 Dec 2020 08:10:34 +0000 https://courses.ideate.cmu.edu/62-362/f2020/?p=10659 This follows a similar idea to my second project, but takes it in a somewhat different direction, fully integrating it into my home network using the MQTT protocol. For those interested in what MQTT is, have a look here.

Images

Here are the three PC nodes in the network…

Jarvis (Iron Man reference), which is the system I usually work at…

Phoenix, which was the codename for this laptop when it was being developed. It sits in the same room as Jarvis, so their temps have effects on each other…

… and Tars (Interstellar reference). This hosts a Minecraft server and resides in my basement… quite dusty at the moment thanks to the kitchen renovation taking place directly above…

Here’s the the Arduino-powered gateway, which can be connected to any PC to interface with the Arduino MQTT bridge. It has been repurposed from the previous project.

The front of the device…

The rear of the device…

And here are a shot of the network in action…

Here’s a shot of the 3 PC nodes interacting with each other. Right now, they’re all running great, sending each other encouraging messages.

And here’s a shot of what might happen if you neglect your own personal health too much, leading the PCs to go on-strike…

While this is just “Wizard of Ozzed” for the sake of having a quick and dirty example (as well as some humor in my choice of commands…), this is something that can happen if you don’t take care of yourself.

Description

There’s quite a bit to cover here, so bear with me.

As I had stated in the briefing for my previous project, I tend to take much better care of my computers than I do myself (especially when it comes to sleep schedule). The previous idea was one way of attempting to remedy this, but it had some shortcomings. Namely, it was far too easy to game the system with regard to reporting temperatures of the systems, and it had no connection to the systems in order to assist in dealing with certain situations (i.e., when a system is running too hot, what do you do?).

The goal is to allow any and all nodes in the network to communicate their vitals to each other. There are 3 PC nodes (all running Manjaro Linux), as well as a human node. This human node consists of an Arduino (for capturing my vitals at any given moment using the same sensors as in the last project), as well as a PC for reading the output of the Arduino and sending it to the other nodes in the network via the MQTT protocol.

Most of the time, the systems simply communicate their vitals with each other and send each other little messages based on what may be happening at that moment. For instance, one PC may be under heavy load, but its temperatures are in-check. The other PCs will see that and send a message to spur it on further–something along the lines of, “Hey, you’re doing great!” (because especially in times like these, we can all use a little bit of affirmation).

In another instance, a PC may be under very little load, but the temps could still be high. This would indicate a serious problem with that machine (perhaps a fan failure or serious heat in the room). This would cause the other PCs in the network to “sound the alarm” and notify me that something is seriously wrong.

Certain audible responses are triggered based on what the situation is. For instance, if a PC is “sounding the alarm,” it will ring the terminal bell and say something along the lines of “HELP! I need somebody! Not just anybody!” (in a text-to-speech voice for added comedic effect).

Now, that only covers PC-to-PC interaction. At any given point, I can enter the network and report my own vitals via the Arduino connected to a PC. Like with the last project, these are Galvanic Skin Response/Resistance (GSR), heart rate, when I went to sleep the previous night, and for how long I slept. If my GSR is too low or heart rate is too high (indicating that I am stressed), then the PCs will see this and offer words of encouragement.

On the other hand, if I went to sleep at an unreasonable hour the previous night, then that will count as a strike. Strikes last until the end of the week. If I accumulate three strikes, then the computers go on-strike and refuse to perform certain actions. Pulling an all-nighter results in an automatic three strikes.

Progress

One of the things that required quite a bit of trial and error was pulling the necessary data from terminal output. I was simply capturing the output of the sensors command (to get the CPU temperature) and nvidia-smi (to get GPU temperature and utilization, since I have Nvidia GPUs in my systems.

Initially, when the GPU was under 100% load, it would read as 00. This was an indexing issue and was easy to fix.

This happened for no apparent reason. It’s possible that the program just received garbage data from nvidia-smi…

This didn’t go over well with my parents… more on that later…

Since there wasn’t a bunch to do on the Arduino side of things and most of my issues were in Python, that’s all I have for in-progress photo documentation, sadly.

Process Reflection

As with everything I’ve done thus far, time management was a major issue. I’m going to save the details on that, though, as I’ve gone into it already in documentation for my previous works.

Getting the PCs to talk to each other over MQTT was far easier than I had anticipated thanks to Garth Zeglin’s work on the MQTT bridge and server for IDeATe. He did the majority of the dirty work, and there was no need to reinvent the wheel, so I borrowed liberally from his original code.

Something much more difficult was getting the system to consistently read the same sensor data. I’m not sure why, but occasionally, nvidia-smi will output garbage for the GPU utilization. This led to crashes that were initially very hard to track down, but after a while, I simply added an empty string check where necessary–the crashes are gone now.

Another difficult part was figuring out what to actually do based on certain scenarios. I wanted to have a somewhat whimsical feel to the PC reactions regardless of what the situation was. Having heard RZach use a somewhat comical text-to-speech voice during class throughout the semester to notify us when our presentation time slot was ending, I decided to follow a similar path and have the machines output responses to me via text-to-speech.

Unfortunately, that did not go over well with my parents when they heard Tars say “Seth, what the hell are you doing? I’m burning over here!” in a robotic voice… right in the middle of their Netflix-watching time. As a result, that code has been omitted for the time being.

Perhaps when I get my own place, I can revisit that. For now, I have to stick with more discrete notifications like plain text messages over MQTT.

Code

Here are the scripts that run on the 3 PC nodes. They are all very similar, but have minor differences to account for Intel vs. AMD CPU (different way of reporting temps), as well as GPU vitals reporting.

For Jarvis…

#!/usr/bin/env python3
"""A PyQt5 GUI utility to monitor and send MQTT server messages."""

################################################################
# Written in 2018-2020 by Garth Zeglin <garthz@cmu.edu>

# To the extent possible under law, the author has dedicated all copyright
# and related and neighboring rights to this software to the public domain
# worldwide. This software is distributed without any warranty.

# You should have received a copy of the CC0 Public Domain Dedication along with this software.
# If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.

################################################################
# standard Python libraries
from __future__ import print_function
import os, sys, struct, time, logging, functools, queue, signal, getpass, psutil

# documentation: https://doc.qt.io/qt-5/index.html
# documentation: https://www.riverbankcomputing.com/static/Docs/PyQt5/index.html
from PyQt5 import QtCore, QtGui, QtWidgets, QtNetwork

# documentation: https://www.eclipse.org/paho/clients/python/docs/
import paho.mqtt.client as mqtt

# default logging output
log = logging.getLogger('main')

# logger to pass to the MQTT library
mqtt_log = logging.getLogger('mqtt')
mqtt_log.setLevel(logging.WARNING)

# IDeATE server instances, as per https://mqtt.ideate.cmu.edu/#ports

ideate_ports = { 8884 : '16-223',
                 8885 : '16-375',
                 8886 : '60-223',
                 8887 : '62-362',
}

mqtt_rc_codes = ['Success', 'Incorrect protocol version', 'Invalid client identifier', 'Server unavailable', 'Bad username or password', 'Not authorized']
counter = 0


################################################################
class MainApp(object):
    """Main application object holding any non-GUI related state."""

    def __init__(self):

        # Attach a handler to the keyboard interrupt (control-C).
        signal.signal(signal.SIGINT, self._sigint_handler)

        # load any available persistent application settings
        QtCore.QCoreApplication.setOrganizationName("IDeATe")
        QtCore.QCoreApplication.setOrganizationDomain("ideate.cmu.edu")
        QtCore.QCoreApplication.setApplicationName('mqtt_monitor')
        self.settings = QtCore.QSettings()

        # uncomment to restore 'factory defaults'
        # self.settings.clear()

        # MQTT server settings
        self.hostname = self.settings.value('mqtt_host', 'mqtt.ideate.cmu.edu')
        self.systemHostname = os.popen('uname -n').read().strip()
        self.portnum  = self.settings.value('mqtt_port', 8887)
        self.portnum = int(self.portnum)
        self.username = self.settings.value('mqtt_user', 'students')
        self.password = self.settings.value('mqtt_password', 'Arduino')

        # Create a default subscription based on the username.  The hash mark is a wildcard.
        username = getpass.getuser()
        self.subscription = self.settings.value('mqtt_subscription', username + '/#')

        # default message to send
        self.topic = self.settings.value('mqtt_topic', username)
        self.payload = self.settings.value('mqtt_payload', 'hello')

        # Initialize the MQTT client system
        self.client = mqtt.Client()
        self.client.enable_logger(mqtt_log)
        self.client.on_log = self.on_log
        self.client.on_connect = self.on_connect
        self.client.on_disconnect = self.on_disconnect
        self.client.on_message = self.on_message
        self.client.tls_set()
        return

    ################################################################
    def app_is_exiting(self):
        if self.client.is_connected():
            self.client.disconnect()
            self.client.loop_stop()

    def _sigint_handler(self, signal, frame):
        print("Keyboard interrupt caught, running close handlers...")
        self.app_is_exiting()
        sys.exit(0)

    ################################################################
    def set_server_name(self, name):
        self.hostname = name
        self.settings.setValue('mqtt_host', name)

    def set_server_port(self, value):
        self.portnum = value
        self.settings.setValue('mqtt_port', self.portnum)

    def set_username(self, name):
        self.username = name
        self.settings.setValue('mqtt_user', name)

    def set_password(self, name):
        self.password = name
        self.settings.setValue('mqtt_password', name)

    def connect_to_mqtt_server(self):
        if self.client.is_connected():
            return
        else:
            if self.portnum is None:
                print("Warning: Please specify the server port before attempting connection.")
            else:
                print("Info: Initiating MQTT connection to %s:%d" % (self.hostname, self.portnum))
                self.client.username_pw_set(self.username, self.password)
                self.client.connect_async(self.hostname, self.portnum)
                self.client.loop_start()

    def disconnect_from_mqtt_server(self):
        if self.client.is_connected():
            self.client.disconnect()
        else:
            print("Warning: Not connected.")
        self.client.loop_stop()

    ################################################################
    # The callback for when the broker responds to our connection request.
    def on_connect(self, client, userdata, flags, rc):
        print("Info: Connected to server with with flags: %s, result code: %s" % (flags, rc))

        if rc == 0:
            print("Info: Connection succeeded.")

        elif rc > 0:
            if rc < len(mqtt_rc_codes):
                print("Warning: Connection failed with error: %s", mqtt_rc_codes[rc])
            else:
                print("Warning: Connection failed with unknown error %d", rc)

        # Subscribing in on_connect() means that if we lose the connection and reconnect then subscriptions will be renewed.
        client.subscribe(self.subscription)
        return

    # The callback for when the broker responds with error messages.
    def on_log(client, userdata, level, buf):
        print("Info: on_log level %s: %s", level, userdata)
        return

    def on_disconnect(self, client, userdata, rc):
        print("Info: disconnected")
        return

    # The callback for when a message has been received on a topic to which this
    # client is subscribed.  The message variable is a MQTTMessage that describes
    # all of the message parameters.

    # Some useful MQTTMessage fields: topic, payload, qos, retain, mid, properties.
    #   The payload is a binary string (bytes).
    #   qos is an integer quality of service indicator (0,1, or 2)
    #   mid is an integer message ID.

    def on_message(self, client, userdata, msg):
        global counter
        if msg.topic == "leffen/pc_vitals":
            if counter == 5:
                counter = 0
                msgList = str(msg.payload)
                msgList = msgList.split(",")
                msgList[-1] = msgList[-1][0:2]
                msgList[-1] = msgList[-1].strip()
                if msgList[0][2:] != self.systemHostname:
                    if (float(msgList[1]) < 40.0 and float(msgList[2]) >= 85.0) or \
                        (int(msgList[3]) >= 85 and (len(msgList) < 5 or (msgList[4] != "" and int(msgList[4]) < 40))):
                        # This is the critical case, where something is probably seriously wrong with the system...
                        sys.stdout.write('\a') # Ring terminal bell
                        sys.stdout.flush()
                        self.send_message("leffen/pc_chatter", "Hey %s you good? No? I'm gonna call the Fire Apartment then... WOO-ooo-WOO-ooo!" % msgList[0])
                    elif (float(msgList[1]) >= 40.0 and float(msgList[2]) >= 85.0) or \
                        (int(msgList[3]) >= 85 and (len(msgList) < 5 or (msgList[4] != "" and int(msgList[4]) >= 40))):
                        # This is the case where the system is under load and getting a bit hot...
                        self.send_message("leffen/pc_chatter", "%s looks like you're running a little hot... why not take a break? I think you've earned it." % msgList[0])
                    elif (float(msgList[1]) >= 80.0 and float(msgList[2]) < 85.0) or \
                        (int(msgList[3]) < 85 and (len(msgList) < 5 or (msgList[4] != "" and int(msgList[4]) >= 80 or \
                            int(msgList[4]) == 0))):
                        # This is the case where the system is under load and temps are in-check...
                        self.send_message("leffen/pc_chatter", "Looks like you're on it %s... keep it up!" % msgList[0])
        elif msg.topic == "leffen/human_vitals":
            msgList = str(msg.payload)
            msgList = msgList.split(",")
            if (float(msgList[0]) < 100 or float(msgList[1]) >= 130):
                self.send_message("leffen/to_human", "Hey man, looks like you need a break.")
            if (int(msgList[2]) == 0):
                self.send_message("leffen/to_human", "Alright, if you aren't gonna take care of yourself, then we're going on strike!")
            elif (int(msgList[3]) < 7 or int(msgList[3]) >= 12):
                self.send_message("leffen/to_human", "Come on, man. Get better sleep!")
        counter += 1                
        print("{%s} %s" % (msg.topic, msg.payload))
        return

    ################################################################
    def set_subscription(self, sub):
        if self.client.is_connected():
            self.client.unsubscribe(self.subscription)
            try:
                self.client.subscribe(sub)
                self.subscription = sub
                self.settings.setValue('mqtt_subscription', sub)
            except ValueError:
                print("Error: Invalid subscription string, not changed.")
                self.client.subscribe(self.subscription)
        else:
            self.subscription = sub
            self.settings.setValue('mqtt_subscription', sub)

    def set_topic(self, sub):
        self.topic = sub
        self.settings.setValue('mqtt_topic', sub)

    def send_message(self, topic, payload):
        if self.client.is_connected():
            self.client.publish(topic, payload)
        else:
            print("Error: Not connected.")
        self.payload = payload
        self.settings.setValue('mqtt_payload', payload)

    ################################################################

def main():
    # Optionally add an additional root log handler to stream messages to the
    # terminal console.
    if False:
        console_handler = logging.StreamHandler()
        console_handler.setLevel(logging.DEBUG)
        console_handler.setFormatter(logging.Formatter('%(levelname)s:%(name)s: %(message)s'))
        logging.getLogger().addHandler(console_handler)

    # initialize the Qt system itself
    app = QtWidgets.QApplication(sys.argv)

    # create the main application controller
    main = MainApp()
    main.connect_to_mqtt_server()

    # run the event loop until the user is done
    print("Info: Starting event loop.")
    while True:
        rawCPUTemp = os.popen('sensors').read()
        rawGPU = os.popen('nvidia-smi').read()
        cpuTempLine = rawCPUTemp.splitlines()[5].strip()
        gpuTempLine = rawGPU.splitlines()[9].strip()
        gpuUtilLine = rawGPU.splitlines()[9].strip()
        cpuUtil = psutil.cpu_percent()
        cpuTempstr = cpuTempLine[15:19]
        gpuTempstr = gpuTempLine[8:10]
        cpuUtilstr = str(cpuUtil)
        gpuUtilstr = gpuUtilLine[60:63]
        cpuTemp = float(cpuTempstr)
        gpuTemp = float(gpuTempstr)
        main.send_message("leffen/pc_vitals", main.systemHostname + "," +
            cpuUtilstr + "," + cpuTempstr + "," + gpuTempstr + "," + gpuUtilstr)
        time.sleep(10)
    sys.exit(app.exec_())

################################################################
# Main script follows. This sequence is executed when the script is initiated
# from the command line.

if __name__ == "__main__":
    main()

For Phoenix…

#!/usr/bin/env python3
"""A PyQt5 GUI utility to monitor and send MQTT server messages."""

################################################################
# Written in 2018-2020 by Garth Zeglin <garthz@cmu.edu>

# To the extent possible under law, the author has dedicated all copyright
# and related and neighboring rights to this software to the public domain
# worldwide. This software is distributed without any warranty.

# You should have received a copy of the CC0 Public Domain Dedication along with this software.
# If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.

################################################################
# standard Python libraries
from __future__ import print_function
import os, sys, struct, time, logging, functools, queue, signal, getpass, psutil

# documentation: https://doc.qt.io/qt-5/index.html
# documentation: https://www.riverbankcomputing.com/static/Docs/PyQt5/index.html
from PyQt5 import QtCore, QtGui, QtWidgets, QtNetwork

# documentation: https://www.eclipse.org/paho/clients/python/docs/
import paho.mqtt.client as mqtt

# default logging output
log = logging.getLogger('main')

# logger to pass to the MQTT library
mqtt_log = logging.getLogger('mqtt')
mqtt_log.setLevel(logging.WARNING)

# IDeATE server instances, as per https://mqtt.ideate.cmu.edu/#ports

ideate_ports = { 8884 : '16-223',
                 8885 : '16-375',
                 8886 : '60-223',
                 8887 : '62-362',
}

mqtt_rc_codes = ['Success', 'Incorrect protocol version', 'Invalid client identifier', 'Server unavailable', 'Bad username or password', 'Not authorized']
counter = 0


################################################################
class MainApp(object):
    """Main application object holding any non-GUI related state."""

    def __init__(self):

        # Attach a handler to the keyboard interrupt (control-C).
        signal.signal(signal.SIGINT, self._sigint_handler)

        # load any available persistent application settings
        QtCore.QCoreApplication.setOrganizationName("IDeATe")
        QtCore.QCoreApplication.setOrganizationDomain("ideate.cmu.edu")
        QtCore.QCoreApplication.setApplicationName('mqtt_monitor')
        self.settings = QtCore.QSettings()

        # uncomment to restore 'factory defaults'
        # self.settings.clear()

        # MQTT server settings
        self.hostname = self.settings.value('mqtt_host', 'mqtt.ideate.cmu.edu')
        self.systemHostname = os.popen('uname -n').read().strip()
        self.portnum  = self.settings.value('mqtt_port', 8887)
        self.portnum = int(self.portnum)
        self.username = self.settings.value('mqtt_user', 'students')
        self.password = self.settings.value('mqtt_password', 'Arduino')

        # Create a default subscription based on the username.  The hash mark is a wildcard.
        username = getpass.getuser()
        self.subscription = self.settings.value('mqtt_subscription', username + '/#')

        # default message to send
        self.topic = self.settings.value('mqtt_topic', username)
        self.payload = self.settings.value('mqtt_payload', 'hello')

        # Initialize the MQTT client system
        self.client = mqtt.Client()
        self.client.enable_logger(mqtt_log)
        self.client.on_log = self.on_log
        self.client.on_connect = self.on_connect
        self.client.on_disconnect = self.on_disconnect
        self.client.on_message = self.on_message
        self.client.tls_set()
        return

    ################################################################
    def app_is_exiting(self):
        if self.client.is_connected():
            self.client.disconnect()
            self.client.loop_stop()

    def _sigint_handler(self, signal, frame):
        print("Keyboard interrupt caught, running close handlers...")
        self.app_is_exiting()
        sys.exit(0)

    ################################################################
    def set_server_name(self, name):
        self.hostname = name
        self.settings.setValue('mqtt_host', name)

    def set_server_port(self, value):
        self.portnum = value
        self.settings.setValue('mqtt_port', self.portnum)

    def set_username(self, name):
        self.username = name
        self.settings.setValue('mqtt_user', name)

    def set_password(self, name):
        self.password = name
        self.settings.setValue('mqtt_password', name)

    def connect_to_mqtt_server(self):
        if self.client.is_connected():
            return
        else:
            if self.portnum is None:
                print("Warning: Please specify the server port before attempting connection.")
            else:
                print("Info: Initiating MQTT connection to %s:%d" % (self.hostname, self.portnum))
                self.client.username_pw_set(self.username, self.password)
                self.client.connect_async(self.hostname, self.portnum)
                self.client.loop_start()

    def disconnect_from_mqtt_server(self):
        if self.client.is_connected():
            self.client.disconnect()
        else:
            print("Warning: Not connected.")
        self.client.loop_stop()

    ################################################################
    # The callback for when the broker responds to our connection request.
    def on_connect(self, client, userdata, flags, rc):
        print("Info: Connected to server with with flags: %s, result code: %s" % (flags, rc))

        if rc == 0:
            print("Info: Connection succeeded.")

        elif rc > 0:
            if rc < len(mqtt_rc_codes):
                print("Warning: Connection failed with error: %s", mqtt_rc_codes[rc])
            else:
                print("Warning: Connection failed with unknown error %d", rc)

        # Subscribing in on_connect() means that if we lose the connection and reconnect then subscriptions will be renewed.
        client.subscribe(self.subscription)
        return

    # The callback for when the broker responds with error messages.
    def on_log(client, userdata, level, buf):
        print("Info: on_log level %s: %s", level, userdata)
        return

    def on_disconnect(self, client, userdata, rc):
        print("Info: disconnected")
        return

    # The callback for when a message has been received on a topic to which this
    # client is subscribed.  The message variable is a MQTTMessage that describes
    # all of the message parameters.

    # Some useful MQTTMessage fields: topic, payload, qos, retain, mid, properties.
    #   The payload is a binary string (bytes).
    #   qos is an integer quality of service indicator (0,1, or 2)
    #   mid is an integer message ID.

    def on_message(self, client, userdata, msg):
        global counter
        if msg.topic == "leffen/pc_vitals":
            if counter == 5:
                counter = 0
                msgList = str(msg.payload)
                msgList = msgList.split(",")
                msgList[-1] = msgList[-1][0:2]
                msgList[-1] = msgList[-1].strip()
                if msgList[0][2:] != self.systemHostname:
                    if (float(msgList[1]) < 40.0 and float(msgList[2]) >= 85.0) or \
                        (int(msgList[3]) >= 85 and (len(msgList) < 5 or (msgList[4] != "" and int(msgList[4]) < 40))):
                        # This is the critical case, where something is probably seriously wrong with the system...
                        sys.stdout.write('\a') # Ring terminal bell
                        sys.stdout.flush()
                        self.send_message("leffen/pc_chatter", "Hey %s you good? No? I'm gonna call the Fire Apartment then... WOO-ooo-WOO-ooo!" % msgList[0])
                    elif (float(msgList[1]) >= 40.0 and float(msgList[2]) >= 85.0) or \
                        (int(msgList[3]) >= 85 and (len(msgList) < 5 or (msgList[4] != "" and int(msgList[4]) >= 40))):
                        # This is the case where the system is under load and getting a bit hot...
                        self.send_message("leffen/pc_chatter", "%s looks like you're running a little hot... why not take a break? I think you've earned it." % msgList[0])
                    elif (float(msgList[1]) >= 70.0 and float(msgList[2]) < 85.0) or \
                        (int(msgList[3]) < 85 and (len(msgList) < 5 or (msgList[4] != "" and int(msgList[4]) >= 80))):
                        # This is the case where the system is under load and temps are in-check...
                        self.send_message("leffen/pc_chatter", "Looks like you're on it %s... keep it up!" % msgList[0])
        elif msg.topic == "leffen/human_vitals":
            msgList = str(msg.payload)
            msgList = msgList.split(",")
            if (float(msgList[0]) < 100 or float(msgList[1]) >= 130):
                self.send_message("leffen/to_human", "Hey man, looks like you need a break.")
            if (int(msgList[2]) == 0):
                self.send_message("leffen/to_human", "Alright, if you aren't gonna take care of yourself, then we're going on strike!")
            elif (int(msgList[3]) < 7 or int(msgList[3]) >= 12):
                self.send_message("leffen/to_human", "Come on, man. Get better sleep!")
        counter += 1
        print("{%s} %s" % (msg.topic, msg.payload))
        return

    ################################################################
    def set_subscription(self, sub):
        if self.client.is_connected():
            self.client.unsubscribe(self.subscription)
            try:
                self.client.subscribe(sub)
                self.subscription = sub
                self.settings.setValue('mqtt_subscription', sub)
            except ValueError:
                print("Error: Invalid subscription string, not changed.")
                self.client.subscribe(self.subscription)
        else:
            self.subscription = sub
            self.settings.setValue('mqtt_subscription', sub)

    def set_topic(self, sub):
        self.topic = sub
        self.settings.setValue('mqtt_topic', sub)

    def send_message(self, topic, payload):
        if self.client.is_connected():
            self.client.publish(topic, payload)
        else:
            print("Error: Not connected.")
        self.payload = payload
        self.settings.setValue('mqtt_payload', payload)

    ################################################################

def main():
    # Optionally add an additional root log handler to stream messages to the
    # terminal console.
    if False:
        console_handler = logging.StreamHandler()
        console_handler.setLevel(logging.DEBUG)
        console_handler.setFormatter(logging.Formatter('%(levelname)s:%(name)s: %(message)s'))
        logging.getLogger().addHandler(console_handler)

    # initialize the Qt system itself
    app = QtWidgets.QApplication(sys.argv)

    # create the main application controller
    main = MainApp()
    main.connect_to_mqtt_server()

    # run the event loop until the user is done
    print("Info: Starting event loop.")
    #rawCPU = os.system("sensors"
    while True:
        rawCPUTemp = os.popen('sensors').read()
        rawGPU = os.popen('nvidia-smi').read()
        cpuTempLine = rawCPUTemp.splitlines()[19].strip()
        gpuTempLine = rawGPU.splitlines()[9].strip()
        cpuUtil = psutil.cpu_percent()
        gpuUtilLine = rawGPU.splitlines()[9].strip()
        cpuTempstr = cpuTempLine[16:20]
        gpuTempstr = gpuTempLine[8:10]
        cpuUtilstr = str(cpuUtil)
        gpuUtilstr = gpuUtilLine[60:63]
        cpuTemp = float(cpuTempstr)
        gpuTemp = float(gpuTempstr)
        cpuUtil = float(cpuUtilstr)
        main.send_message("leffen/pc_vitals", main.systemHostname + "," +
            cpuUtilstr + "," + cpuTempstr + "," + gpuTempstr + "," + gpuUtilstr)
        time.sleep(10)
    sys.exit(app.exec_())

################################################################
# Main script follows. This sequence is executed when the script is initiated
# from the command line.

if __name__ == "__main__":
    main()

and for Tars…

#!/usr/bin/env python3
"""A PyQt5 GUI utility to monitor and send MQTT server messages."""

################################################################
# Written in 2018-2020 by Garth Zeglin <garthz@cmu.edu>

# To the extent possible under law, the author has dedicated all copyright
# and related and neighboring rights to this software to the public domain
# worldwide. This software is distributed without any warranty.

# You should have received a copy of the CC0 Public Domain Dedication along with this software.
# If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.

################################################################
# standard Python libraries
from __future__ import print_function
import os, sys, struct, time, logging, functools, queue, signal, getpass, psutil

# documentation: https://doc.qt.io/qt-5/index.html
# documentation: https://www.riverbankcomputing.com/static/Docs/PyQt5/index.html
from PyQt5 import QtCore, QtGui, QtWidgets, QtNetwork

# documentation: https://www.eclipse.org/paho/clients/python/docs/
import paho.mqtt.client as mqtt

# default logging output
log = logging.getLogger('main')

# logger to pass to the MQTT library
mqtt_log = logging.getLogger('mqtt')
mqtt_log.setLevel(logging.WARNING)

# IDeATE server instances, as per https://mqtt.ideate.cmu.edu/#ports

ideate_ports = { 8884 : '16-223',
                 8885 : '16-375',
                 8886 : '60-223',
                 8887 : '62-362',
}

mqtt_rc_codes = ['Success', 'Incorrect protocol version', 'Invalid client identifier', 'Server unavailable', 'Bad username or password', 'Not authorized']
counter = 0


################################################################
class MainApp(object):
    """Main application object holding any non-GUI related state."""

    def __init__(self):

        # Attach a handler to the keyboard interrupt (control-C).
        signal.signal(signal.SIGINT, self._sigint_handler)

        # load any available persistent application settings
        QtCore.QCoreApplication.setOrganizationName("IDeATe")
        QtCore.QCoreApplication.setOrganizationDomain("ideate.cmu.edu")
        QtCore.QCoreApplication.setApplicationName('mqtt_monitor')
        self.settings = QtCore.QSettings()

        # uncomment to restore 'factory defaults'
        # self.settings.clear()

        # MQTT server settings
        self.hostname = self.settings.value('mqtt_host', 'mqtt.ideate.cmu.edu')
        self.systemHostname = os.popen('uname -n').read().strip()
        self.portnum  = self.settings.value('mqtt_port', 8887)
        self.portnum = int(self.portnum)
        self.username = self.settings.value('mqtt_user', 'students')
        self.password = self.settings.value('mqtt_password', 'Arduino')

        # Create a default subscription based on the username.  The hash mark is a wildcard.
        username = getpass.getuser()
        self.subscription = self.settings.value('mqtt_subscription', username + '/#')

        # default message to send
        self.topic = self.settings.value('mqtt_topic', username)
        self.payload = self.settings.value('mqtt_payload', 'hello')

        # Initialize the MQTT client system
        self.client = mqtt.Client()
        self.client.enable_logger(mqtt_log)
        self.client.on_log = self.on_log
        self.client.on_connect = self.on_connect
        self.client.on_disconnect = self.on_disconnect
        self.client.on_message = self.on_message
        self.client.tls_set()
        return

    ################################################################
    def app_is_exiting(self):
        if self.client.is_connected():
            self.client.disconnect()
            self.client.loop_stop()

    def _sigint_handler(self, signal, frame):
        print("Keyboard interrupt caught, running close handlers...")
        self.app_is_exiting()
        sys.exit(0)

    ################################################################
    def set_server_name(self, name):
        self.hostname = name
        self.settings.setValue('mqtt_host', name)

    def set_server_port(self, value):
        self.portnum = value
        self.settings.setValue('mqtt_port', self.portnum)

    def set_username(self, name):
        self.username = name
        self.settings.setValue('mqtt_user', name)

    def set_password(self, name):
        self.password = name
        self.settings.setValue('mqtt_password', name)

    def connect_to_mqtt_server(self):
        if self.client.is_connected():
            return
        else:
            if self.portnum is None:
                print("Warning: Please specify the server port before attempting connection.")
            else:
                print("Info: Initiating MQTT connection to %s:%d" % (self.hostname, self.portnum))
                self.client.username_pw_set(self.username, self.password)
                self.client.connect_async(self.hostname, self.portnum)
                self.client.loop_start()

    def disconnect_from_mqtt_server(self):
        if self.client.is_connected():
            self.client.disconnect()
        else:
            print("Warning: Not connected.")
        self.client.loop_stop()

    ################################################################
    # The callback for when the broker responds to our connection request.
    def on_connect(self, client, userdata, flags, rc):
        print("Info: Connected to server with with flags: %s, result code: %s" % (flags, rc))

        if rc == 0:
            print("Info: Connection succeeded.")

        elif rc > 0:
            if rc < len(mqtt_rc_codes):
                print("Warning: Connection failed with error: %s", mqtt_rc_codes[rc])
            else:
                print("Warning: Connection failed with unknown error %d", rc)

        # Subscribing in on_connect() means that if we lose the connection and reconnect then subscriptions will be renewed.
        client.subscribe(self.subscription)
        return

    # The callback for when the broker responds with error messages.
    def on_log(client, userdata, level, buf):
        print("Info: on_log level %s: %s", level, userdata)
        return

    def on_disconnect(self, client, userdata, rc):
        print("Info: disconnected")
        return

    # The callback for when a message has been received on a topic to which this
    # client is subscribed.  The message variable is a MQTTMessage that describes
    # all of the message parameters.

    # Some useful MQTTMessage fields: topic, payload, qos, retain, mid, properties.
    #   The payload is a binary string (bytes).
    #   qos is an integer quality of service indicator (0,1, or 2)
    #   mid is an integer message ID.

    def on_message(self, client, userdata, msg):
        global counter
        if msg.topic == "leffen/pc_vitals":
            if counter == 6:
                counter = 0
                msgList = str(msg.payload)
                msgList = msgList.split(",")
                msgList[-1] = msgList[-1][0:2]
                msgList[-1] = msgList[-1].strip()
                if msgList[0][2:] != self.systemHostname:
                    if (float(msgList[1]) < 40.0 and float(msgList[2]) >= 85.0) or \
                        (int(msgList[3]) >= 85 and (len(msgList) < 5 or (msgList[4] != "" and int(msgList[4]) < 40))):
                        # This is the critical case, where something is probably seriously wrong with the system...
                        sys.stdout.write('\a') # Ring terminal bell
                        sys.stdout.flush()
                        self.send_message("leffen/pc_chatter", "Hey %s you good? No? I'm gonna call the Fire Apartment then... WOO-ooo-WOO-ooo!" % msgList[0])
                    elif (float(msgList[1]) >= 40.0 and float(msgList[2]) >= 85.0) or \
                        (int(msgList[3]) >= 85 and (len(msgList) < 5 or (msgList[4] != "" and int(msgList[4]) >= 40))):
                        # This is the case where the system is under load and getting a bit hot...
                        self.send_message("leffen/pc_chatter", "%s looks like you're running a little hot... why not take a break? I think you've earned it." % msgList[0])
                    elif (float(msgList[1]) >= 80.0 and float(msgList[2]) < 85.0) or \
                        (int(msgList[3]) < 85 and (len(msgList) < 5 or (msgList[4] != "" and int(msgList[4]) >= 80))):
                        # This is the case where the system is under load and temps are in-check...
                        self.send_message("leffen/pc_chatter", "Looks like you're on it %s... keep it up!" % msgList[0])
        elif msg.topic == "leffen/human_vitals":
            msgList = str(msg.payload)
            msgList = msgList.split(",")
            if (float(msgList[0]) < 100 or float(msgList[1]) >= 130):
                self.send_message("leffen/to_human", "Hey man, looks like you need a break.")
            if (int(msgList[2]) == 0):
                self.send_message("leffen/to_human", "Alright, if you aren't gonna take care of yourself, then we're going on strike!")
            elif (int(msgList[3]) < 7 or int(msgList[3]) >= 12):
                self.send_message("leffen/to_human", "Come on, man. Get better sleep!")
        counter += 1
        print("{%s} %s" % (msg.topic, msg.payload))
        return

    ################################################################
    def set_subscription(self, sub):
        if self.client.is_connected():
            self.client.unsubscribe(self.subscription)
            try:
                self.client.subscribe(sub)
                self.subscription = sub
                self.settings.setValue('mqtt_subscription', sub)
            except ValueError:
                print("Error: Invalid subscription string, not changed.")
                self.client.subscribe(self.subscription)
        else:
            self.subscription = sub
            self.settings.setValue('mqtt_subscription', sub)

    def set_topic(self, sub):
        self.topic = sub
        self.settings.setValue('mqtt_topic', sub)

    def send_message(self, topic, payload):
        if self.client.is_connected():
            self.client.publish(topic, payload)
        else:
            print("Error: Not connected.")
        self.payload = payload
        self.settings.setValue('mqtt_payload', payload)

    ################################################################

def main():
    # Optionally add an additional root log handler to stream messages to the
    # terminal console.
    if False:
        console_handler = logging.StreamHandler()
        console_handler.setLevel(logging.DEBUG)
        console_handler.setFormatter(logging.Formatter('%(levelname)s:%(name)s: %(message)s'))
        logging.getLogger().addHandler(console_handler)

    # initialize the Qt system itself
    app = QtWidgets.QApplication(sys.argv)

    # create the main application controller
    main = MainApp()
    main.connect_to_mqtt_server()

    # run the event loop until the user is done
    print("Info: Starting event loop.")
    while True:
        rawCPUTemp = os.popen('sensors').read()
        rawGPU = os.popen('nvidia-smi').read()
        cpuTempLine = rawCPUTemp.splitlines()[31].strip()
        gpuTempLine = rawGPU.splitlines()[9].strip()
        cpuUtil = psutil.cpu_percent()
        cpuTempstr = cpuTempLine[15:19]
        gpuTempstr = gpuTempLine[8:10]
        cpuUtilstr = str(cpuUtil)
        cpuTemp = float(cpuTempstr)
        gpuTemp = float(gpuTempstr)
        main.send_message("leffen/pc_vitals", main.systemHostname + "," +
            cpuUtilstr + "," + cpuTempstr + "," + gpuTempstr)
        time.sleep(10)
    sys.exit(app.exec_())

################################################################
# Main script follows. This sequence is executed when the script is initiated
# from the command line.

if __name__ == "__main__":
    main()

Here’s the code that runs on the Arduino. It’s nothing fancy–it’s just sending the relevant data as a comma-separated string over serial, which is then picked up by the computer that the Arduino is connected to.

/*
   Human Gateway (62-362 Project 3)
   Seth Geiser (sgeiser)

   Collaboration: Same as Project 2

   GSR Sensor: https://wiki.seeedstudio.com/Grove-GSR_Sensor/
   MAX30102 PulseOx: Example 5: Heart-rate
   Keypad: Same as Project 1, Workaholic's Clock
   DFPlayer Mini: Same as Project 1, Workaholic's Clock

   This code initializes a keypad, 20x4 I2C LCD, Pulse
   Oximeter, Grove GSR sensor, and a DFRobot DFPlayer
   Mini. The keypad is for entering when I went to sleep an
   how long I slept. The GSR sensor measures galvanic skin
   response, similar to a polygraph test. The Pulse Oximeter
   is currently only being used as a heart-rate monitor,
   since the SPO2 functionality appears to not be
   working right now (tested with the SPO2 example
   code).

   Pin mapping:

   pin      | mode       | description
   ---------|------------|------------
   SDA/SCL   I2C          MAX30102 PulseOx + 20x4 I2C LCD
   2-8       Drive/Sense  Keypad
   9         RX           DFPlayer Mini TX
   10        TX           DFPlayer Mini RX
   A0        INPUT        GSR Sensor (through Grove shield)
*/

#include <SoftwareSerial.h>
#include <DFRobotDFPlayerMini.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Keypad.h>
#include <MAX30105.h>
#include <heartRate.h>

SoftwareSerial mySoftwareSerial(9, 10); // RX, TX
DFRobotDFPlayerMini myDFPlayer;

MAX30105 particleSensor;

LiquidCrystal_I2C lcd(0x27, 20, 4);
// set the LCD address to 0x27 for 20x4 display

char customKey = 0;
const byte ROWS = 4;
const byte COLS = 3;
char hexaKeys[ROWS][COLS] = {
  {'1', '2', '3'},
  {'4', '5', '6'},
  {'7', '8', '9'},
  {'*', '0', '#'}
};

byte rowPins[ROWS] = {5, 3, 2, 7};
byte colPins[COLS] = {4, 8, 6};
Keypad customKeypad = Keypad(makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS);

const byte RATE_SIZE = 4; // Increase this for more averaging. 4 is good.
byte rates[RATE_SIZE]; // Array of heart rates
byte rateSpot = 0;
long lastBeat = 0; // Time at which the last beat occurred

float beatsPerMinute;
int beatAvg;

const int GSR = A0;
int sensorValue = 0;
int gsr_average = 0;

int j = 0;
int sleepStart;
int sleepAmount;

// For accepting input from the keypad, these get converted
// to the ints above...
char sleep[2];

void(* resetFunc) (void) = 0; // reset the Arduino when done
void setup() {
  lcd.init();
  lcd.backlight();
  mySoftwareSerial.begin(9600);
  Serial.begin(115200);
  Serial.println("Human,Arduino UNO R3 SMD Connected...");

  lcd.setCursor(0, 0);
  lcd.print(F("Arduino UNO R3 SMD"));
  lcd.setCursor(0, 1);
  lcd.print(F("Starting DFPlayer..."));
  lcd.setCursor(0, 2);
  lcd.print(F("Starting MAX30102..."));
  lcd.setCursor(0, 3);
  lcd.print(F("Please wait..."));
  delay(2000);
  // Initialize sensor
  if (!particleSensor.begin(Wire, I2C_SPEED_FAST))
    // Use default I2C port, 400kHz speed
  {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print(F("MAX30102 not found."));
    lcd.setCursor(0, 1);
    lcd.print(F("Please check wiring."));
    while (1);
  }

  particleSensor.setup();
  // Configure sensor with default settings
  particleSensor.setPulseAmplitudeRed(0x0A);
  // Turn Red LED to low to indicate sensor is running
  particleSensor.setPulseAmplitudeGreen(0);
  // Turn off Green LED

  if (!myDFPlayer.begin(mySoftwareSerial)) {
    // Use softwareSerial to communicate with mp3.
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print(F("Unable to begin:"));
    lcd.setCursor(0, 1);
    lcd.print(F("Please check wiring!"));
    lcd.setCursor(0, 2);
    lcd.print(F("Please insert SD!"));
  }
  lcd.setCursor(0, 3);
  lcd.print(F("DFPlayerMini online."));
  myDFPlayer.volume(20);  // Set volume value. From 0 to 30
  delay(2000);
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(F("CMU IDeATe PhysComp"));
  lcd.setCursor(0, 1);
  lcd.print(F("Human Gateway"));
  lcd.setCursor(0, 2);
  lcd.print(F("Powered by sgeiser"));
  lcd.setCursor(0, 3);
  lcd.print(F("BOOTING DEVICE..."));
  delay(2500);
  myDFPlayer.play(1); // 0001_ps2.mp3
  delay(2500);
  lcd.clear();
  customKey = customKeypad.getKey();
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(F("Input when you went"));
  lcd.setCursor(0, 1);
  lcd.print(F("to sleep and how"));
  lcd.setCursor(0, 2);
  lcd.print(F("long you slept in"));
  lcd.setCursor(0, 3);
  lcd.print(F("24-hour time."));
  delay(5000);
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(F("If any value is less"));
  lcd.setCursor(0, 1);
  lcd.print(F("than 10, then prefix"));
  lcd.setCursor(0, 2);
  lcd.print(F("with a zero."));
  delay(5000);
  lcd.clear();
  inputSleepPattern();
}

void loop() {
  customKey = customKeypad.getKey();
  long sum = 0;
  for (int i = 0; i < 10; i++) {
    sensorValue = analogRead(GSR);
    sum += sensorValue;
  }
  gsr_average = sum / 10;
  long irValue = particleSensor.getIR();

  if (checkForBeat(irValue) == true)
  {
    // We sensed a beat!
    long delta = millis() - lastBeat;
    lastBeat = millis();

    beatsPerMinute = 60 / (delta / 1000.0);

    if (beatsPerMinute < 255 && beatsPerMinute > 20)
    {
      rates[rateSpot++] = (byte)beatsPerMinute;
      // Store this reading in the array
      rateSpot %= RATE_SIZE; // Wrap variable

      // Take average of readings
      beatAvg = 0;
      for (byte x = 0 ; x < RATE_SIZE ; x++)
        beatAvg += rates[x];
      beatAvg /= RATE_SIZE;
    }
  }
  lcd.setCursor(0, 0);
  lcd.print((String)"Average GSR: " + gsr_average + "    ");
  lcd.setCursor(0, 1);
  lcd.print((String)"Average BPM: " + beatAvg + "    ");
  lcd.setCursor(0, 2);
  lcd.print(F("#: Assess vitals"));
  if (customKey == '#') {
    lcd.clear();
    assess();
  }
}

void inputSleepStart() {
  lcd.setCursor(0, 0);
  lcd.print(F("Sleep start: "));
  while (j < 2) {
    char key = customKeypad.getKey();
    if (key) {
      sleep[j++] = key;
      lcd.print(key);
    }
    key = 0;
  }
  sleepStart = atoi(sleep);
  // Convert the char array to int and store it. Note
  // that atoi is deprecated, am going to change it soon.
  j = 0;
}

void inputSleepAmount() {
  lcd.setCursor(0, 1);
  lcd.print(F("Sleep amount: "));
  while (j < 2) {
    char key = customKeypad.getKey();
    if (key) {
      sleep[j++] = key;
      lcd.print(key);
    }
    key = 0;
  }
  sleepAmount = atoi(sleep);
  // Convert the char array to int and store it. Note
  // that atoi is deprecated, am going to change it soon.
  j = 0;
}

// I am not fond of how I wrote these two functions.
// I hope to condense them.

void inputSleepPattern() {
  inputSleepStart();
  inputSleepAmount();
  lcd.clear();
}

void assess() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.setCursor(0, 2);
  lcd.print(F("Checking your vitals"));
  delay(2500);
  if (gsr_average > 500) {
    lcd.setCursor(0, 3);
    lcd.print(F("Could not read GSR."));
    delay(2500);
  }
  if (beatAvg <= 40) {
    lcd.setCursor(0, 3);
    lcd.print(F("Could not get pulse."));
    delay(2500);
  }
  if ((gsr_average < 150 || beatAvg >= 130) && (sleepStart <= 6 || (sleepAmount < 7 || sleepAmount > 12))) {
    allBad();
  }
  else if (gsr_average < 150 || beatAvg >= 130) {
    stressed();
  }
  else if (sleepStart <= 6 || (sleepAmount < 7 || sleepAmount > 12)) {
    badSleep();
  }
  else {
    allGood();
  }
  Serial.println(String(gsr_average) + "," + String(beatAvg) + "," + String(sleepStart) + "," + String(sleepAmount));
  resetFunc();
}

void badSleep() {
  lcd.setCursor(0, 3);
  lcd.print(F("Get better sleep!   "));
  delay(5000);
}

void stressed() {
  lcd.setCursor(0, 3);
  lcd.print(F("You're stressed..."));
  delay(2500);
  lcd.setCursor(0, 3);
  lcd.print(F("Take a break, dude."));
  delay(5000);
}

void allBad() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(F("Oh my God, it's all"));
  lcd.setCursor(0, 1);
  lcd.print(F("broken! Seriously,"));
  lcd.setCursor(0, 2);
  lcd.print(F("you need to get on"));
  lcd.setCursor(0, 3);
  lcd.print(F("top of this..."));
  myDFPlayer.play(5); // 0005_battery_low.mp3
  delay(5000);
}

void allGood() {
  lcd.setCursor(0, 3);
  lcd.print(F("Your vitals are OK!"));
  delay(2500);
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(F("Nicely done! Your"));
  lcd.setCursor(0, 1);
  lcd.print(F("vitals look alright,"));
  lcd.setCursor(0, 2);
  lcd.print(F("and your systems are"));
  lcd.setCursor(0, 3);
  lcd.print(F("running smoothly!"));
  myDFPlayer.play(3); // 0003_scatman_world.mp3
  delay(13000);
}

 

]]>
Remote Consequence https://courses.ideate.cmu.edu/62-362/f2020/project-no-3-remote-consequence/ Tue, 15 Dec 2020 03:46:36 +0000 https://courses.ideate.cmu.edu/62-362/f2020/?p=10583 Sorry, but you do not have permission to view this content. ]]> Sorry, but you do not have permission to view this content. ]]> An Extension of Us https://courses.ideate.cmu.edu/62-362/f2020/an-extension-of-us/ Tue, 15 Dec 2020 03:19:03 +0000 https://courses.ideate.cmu.edu/62-362/f2020/?p=10587 For this piece, “An Extension of Us,” I created two braids where those using it can communicate with each other using a series of inputs and outputs. When one person brings their hand close to the braid, the light will turn on for both of them, when one person squeezes the braid, the other will feel a buzz, and when both people sing loudly enough a song will play for both of them.

An Extension of Us

made in collaboration with my sister Ivana

For a braid to hold, its strands must conform to a gradual taper, or else, without a rubber band or ribbon tightened to its end, it unravels.

In some cases, change must be abrupt. In others, we force it so, getting the pattern all wrong and having to start over.

And I know that there’s no way you would ever leave me stranded, even though I question whether this blood is sufficient reason to keep our lives intertwined. Even though I’m unsure of the difference between intertwined and bound. Even though I’m not sure if those differences even matter. 

A braid, woven tightly enough, becomes rigid, any unevenness obvious.

Think about the time spent crossing these strands, millions of them by now, and think of the banality. Think of the time spent in each other’s presence, chattering to distract, our bond strengthening over days as our fingers weakened over hours, this rite of passage now the basis of our livelihoods.

Question: How many times, in our lives, will we undo? Start over? 

Answer: Every time we notice the tangled mass of hair our lives have become. Hopefully.

I wonder how often you feel lonely when I’m around. I wonder if you ever look at me and think: You’ll never understand.

I am trying to hold the small things and remember that they make up the bulk of our time here and sit, ensconced in the loveliness of them and notice that they are there, innumerable, they’re fine strands intertwined, knotted, waiting to be brushed free.

I am trying not to relegate the good only to memories.

Initially I had wanted to create three braids for myself, my mother and my sister, but ended up creating two. While building this I kept forgetting how to set up the wires and also how to debug which led to an even longer process when getting to the end of it. In the beginning I had planned on having the two braids communicate with one another from a distance, unfortunately there was a problem with setting up the software correctly so then decided to wire the arduinos together so that they could communicate with each other. One of the hardest parts was actually braiding everything together, something that I thought would actually be the easiest. One of the first problems that I had was the placement of everything, which ended up being too long for the braid. This was mainly because the pressure sensor was at the end and since the braid tapered at the end it was not enough to cover it well enough. While I was braiding I kept getting frustrated because of how the hairs kept snagging onto the different pieces, after my like fifth attempt or so I decided to separate the hair and create three separate braids before braiding everything together. Doing this created a different, unique braid to what is usually done in hairstyles, and especially because of all the components that were in which is something that I liked. When I was connecting the braid to my roommate’s hair I expected it to be much heavier than it was but the braid did not feel heavy at all for neither of us, but the fact that we have both had hair extensions throughout most of our lives I’m sure helped with that. When it comes to the portion with the arduino I definitely learned a lot considering how this was my first time interacting with it all. Overall it was a taxing and eye opening project and I really enjoyed it despite all of the hurdles that I experienced.

Arduino Code:

//Big ting
#include “Arduino.h”
#include “SoftwareSerial.h”
#include “DFRobotDFPlayerMini.h”
SoftwareSerial speakerSerial(10, 11); // RX, TX
DFRobotDFPlayerMini myDFPlayer;
void printDetail(uint8_t type, int value);

SoftwareSerial vals2(2, 3); // RX, TX

int RXPIN = 2;
int TXPIN = 3;
int distanceVal2;
int pressureVal2;
int svTotal2;

int soundDetector = A2;
int distanceSensor = A4;
int pressurePin = A5;
int ledPin = 5;
int motorPin = 7;

void setup() {
pinMode(distanceSensor, INPUT);
pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, LOW);

pinMode(pressurePin, INPUT);
pinMode(motorPin, OUTPUT);
digitalWrite(motorPin, LOW);

vals2.begin(9600);
speakerSerial.begin(9600);
Serial.begin(9600);

Serial.println();
Serial.println(F(“DFRobot DFPlayer Mini Demo”));
Serial.println(F(“Initializing DFPlayer … (May take 3~5 seconds)”));
if (!myDFPlayer.begin(speakerSerial)) { //Use softwareSerial to communicate with mp3.
Serial.println(F(“Unable to begin:”));
Serial.println(F(“1.Please recheck the connection!”));
Serial.println(F(“2.Please insert the SD card!”));
while(true);
}
Serial.println(F(“DFPlayer Mini online.”));
myDFPlayer.volume(20); //Set volume value. From 0 to 30*/
}

long svTotal=0;

void loop() {
vals2.listen();
int distanceVal = analogRead(distanceSensor);
int pressureVal = analogRead(pressurePin);
int soundVal = analogRead(soundDetector);
for (int i = 0; i <= 100; i++){
svTotal += soundVal;
delay(1);
}
svTotal=svTotal/100;
//Serial.println(“sound val total”);
//Serial.println(svTotal);

//vals2.println(“hm”);
vals2.println((String)distanceVal+”,”+pressureVal+”,”+svTotal);
if (vals2.available()){
Serial.println(“vals2 data”);
distanceVal2 = vals2.parseInt();
pressureVal2 = vals2.parseInt();
svTotal2 = vals2.parseInt();
}
Serial.print(“distance val: “);
Serial.println(distanceVal); /*
Serial.println(“pressure val”);
Serial.println(pressureVal);
Serial.println(“sound val”);
Serial.println(soundVal);*/

//distance -> light
if ((distanceVal > 10)||(distanceVal2 > 50)) {
digitalWrite(ledPin, HIGH);
delay(3000);
}
digitalWrite(ledPin, LOW);

//pressure -> buzz
if (pressureVal2 > 800){
digitalWrite(motorPin, HIGH);
delay(500);
}
digitalWrite(motorPin, LOW);

//sound -> music
if ((svTotal > 300) && (svTotal2 > 300)) {
myDFPlayer.play(1);
delay(10000);
myDFPlayer.next();
}
svTotal = 0;

}

]]>
In and Out https://courses.ideate.cmu.edu/62-362/f2020/in-and-out/ Tue, 15 Dec 2020 01:41:27 +0000 https://courses.ideate.cmu.edu/62-362/f2020/?p=10563 For this project, “In and Out,” a picture of myself was taken entering and exiting the room over the course of about six days using a motion detector app called iSpyConnect on a laptop. This laptop was placed in the middle of the room on a shoe shelf with the screen off. As a result of this pandemic, and the lockdown with it, I am in my room almost all of the time and this is much more compared to the time I spent prior to the lockdown. My room was only ever used to escape, to take a break from work or to sleep, but now it’s a place that I need to escape from. My workflow and daily habits have completely changed and I am now almost like a shell of my former self, but it’s difficult to escape from this. When it came down to how I presented the final 50 pictures I chose to use a grid and behind the pictures is a gradient from black to white to indicate the time of day and also as a separation marker between the different days. I chose black and white because life after lockdown has been pretty mundane and all the days blur into each other. The second part of the presentation includes pictures of the locations in my place that I might be in, this includes my bed, the bathroom, the kitchen, and the living room. I chose to take pictures of these spaces with me both in and out of them because I can only be in one space at a time. I wanted the viewer to create their own story in a sense of where I was and what I was doing. The last portion of this presentation is a video of the 50 pictures together and what I like about this is how fast the time goes in the video and things just blur into each other.

 

Process Images:

Above are the 3 different positions I was considering when setting up the laptop.

Above are the ways that I setup the camera for the self-portraits.

Me checking the settings of the app to make sure that it was working properly.

 

Reflection:

While doing this project I definitely felt conscious of the way that I interacted with my space, with how my hair looked, and how I was dressed. I did my best to make sure that this did not impact the actions I made but it was something I felt more aware of. The laptop had a camera protector on it but here I was exposing myself 24/7. I was also thinking about how people might judge me once they paid more attention to the times that I woke up or went to sleep. 

In the beginning of this project I was using the laptop and phone that I always use, but at some point they had to be moved so that I could use them. So I did not really like that because that also meant that the pictures could not be taken in the exact same position. Finding a free security app for my iPhone and MacBook was a long process, I was not successful with finding one for my phone but I eventually found one for my MacBook. Since I needed my MacBook more I tried to connect the camera on my phone with the security app on my laptop but unfortunately the app did not detect the motion from my phone. I remembered that I had an extra laptop and if that I could get a charger for it I could use that without having to interfere with it. So I went on another hunt to find an app that could work with it since it was a Dell laptop and came across iSpyConnect. This app took awhile for me to figure out how to use and was much more complicated compared to the one I had used on my MacBook, but it worked out eventually. Since I interacted with the area in front of the camera a good bit, this resulted in over 3000 pictures that were taken, having to sift through all of those to decide the final 50 took awhile and I also kept going back and forth on which part of the walking in and out process that I wanted to use. It was also an interesting process setting the camera up to take the self portraits in the different spaces, depending on the space it took awhile to figure out. The easiest part about this project was definitely coming in and out of my room haha.

Perry mentioned looking at all the pictures in which I interacted with the space in a certain way, for instance right when I put my hand on the door to open, or when I came into my room with a mask on. This is an interesting idea that I would have liked to include as well, to see the way in which I interacted with my space differently or in the same manner.

]]>
Funeral Simulator https://courses.ideate.cmu.edu/62-362/f2020/project-no-3-john-hewitt/ Mon, 14 Dec 2020 22:29:09 +0000 https://courses.ideate.cmu.edu/62-362/f2020/?p=10557 Photos:

Video:

This video shows me placing dolls on a board and pressing a button which spawns them into my Unity scene. This is only a prototype, which is far from perfect, and you can see me fiddle with the position of the blue doll (and later the red doll) after the Arduino failed to pick them up.

Description:

Place dolls on a board and push the ‘spawn’ button, the dolls on said board will then appear in a digital environment (created with Unity). Each doll can be differentiated by their color, both in their physical and digital representation. When the ‘spawn’  button is pushed, the digital environment will be emptied and then refilled with the dolls placed on the board (if any). In the digital environment, the dolls are given instructions to move to random positions inside given boundaries.

Each doll holds a different resistor that can be read by the Arduino, which is what allows the Arduino to determine the dolls color. The Arduino then sends that information to Unity, where it is interpreted and carried out.

Music: Brian Eno – New Space Music (Visualizer)

Progress Images:

These images include some of the 3d models I made as well as  parts of the fabrication process.

Reflection:

There are many events in our lives that we share with our family and friends. Under quarantine however, we are no longer able to share the same physical spaces with one another as we once could. It was my goal, for this project, to simulate these life events as they may have appeared before quarantine. Where representations of our family and friends would be able to share a virtual environment in lieu of a physical one.

I chose to create a funeral setting after attending a zoom call in memorial of my late uncle. I began to picture what my own funeral might look like, and who I hoped would attend.

So I made a board that would essentially act as the invite list, and a few dolls that would represent the people you might invite.

This project is only a prototype of the initial idea. The environment I made was very open to interpretation, and not necessarily personal. In a future iteration, I would make it so that, rather than color, each doll would represent a specific person in your life. I feel that this would drastically change how a person would interact with this project. Specifically, I believe that it would make placing the dolls on the board a much more personal experience. There are many other levels of customization I could hope to incorporate, but to create a literal connection between the dolls at your disposal and the people in your life is perhaps the most significant.

I was glad to be able to incorporate Unity in this project. Getting the Arduino to communicate with Unity was a good learning experience, and I believe will be useful in the future. I used SolidWorks to create the models in the scene as well.  It was very satisfying to be able to bring together these different skills I’ve developed over the semester into one project.

Code:

Arduino:

int pin_1 = A0;
int pin_2 = A1;
int pin_3 = A2;
int pin_4 = A3;

int sim_button = 5;

int array_size = 4;
int pin_list[] = {pin_1, pin_2, pin_3, pin_4};
float pin_readings[] = {0.0, 0.0, 0.0, 0.0};
int send_to_unity[] = {0, 0, 0, 0};

int pin; 
float pin_reading = 0.0;

int raw = 0;
int Vin = 5;
float Vout = 0;
float R_1K = 1000;
float R_unknown = 0;
float buffer = 0;


void setup() {
  Serial.begin(9600);
  
  pinMode(pin_1, INPUT);
  pinMode(pin_2, INPUT);
  pinMode(pin_3, INPUT);
  pinMode(pin_4, INPUT);
  pinMode(sim_button, INPUT);
}

void loop() {
  if (digitalRead(sim_button) == HIGH) {
    for (int i = 0; i < array_size; i++) {
      pin = pin_list[i];
      pin_reading = resistor_read(pin);
      pin_readings[i] = pin_reading;
      send_to_unity[i] = doll_num(pin_reading);
    }
    
    for (int i = 0; i < array_size; i++) {
      Serial.write(i);
      Serial.flush();
      Serial.write(send_to_unity[i]);
      Serial.flush();
    }    
    delay(1000);
  }
}

float resistor_read(int pin) {
  raw = analogRead(pin);
  if(raw){
    buffer = raw * Vin;
    Vout = (buffer)/1024.0;
    buffer = (Vin/Vout) - 1;
    R_unknown = R_1K * buffer;
    return(R_unknown);
  } else {
    return(0.0);
  }
}

int doll_num(float resistor_val) {
  if (0.0 <= resistor_val && resistor_val < 5.0) {
    //0 empty
    return 0;
  } else if (90.0 < resistor_val && resistor_val < 150.0) {
    //100 red
    return 1;
  } else if (300.0 < resistor_val && resistor_val < 500.0) {
    //330 yellow
    return 2;
  } else if (1900.0 < resistor_val && resistor_val < 2600.0) {
    //2k white
    return 3;
  } else if (4000.0 < resistor_val && resistor_val < 6000.0) {
    //5k1 blue
    return 4;
  } else {
    return 0;
  }
}

Unity:

spawn_dolls:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO.Ports;

public class spawn_dolls : MonoBehaviour
{
    
    public SerialPort sp = new SerialPort("/dev/cu.usbmodem14101", 9600);
    public GameObject doll_prefab;

    private int pin_num = 0;
    private int pin_name = 0;

    private GameObject[] doll_list;

    public Material color;

    void Start()
    {
        sp.Open();
        sp.ReadTimeout = 1;

        doll_list = new GameObject[] {null, null, null, null};
    }

    // Update is called once per frame
    void Update()
    {
        if (sp.IsOpen) {
            try 
            { 
                // read the incoming byte:
                pin_num = sp.ReadByte();
                pin_name = sp.ReadByte();

                if (pin_num == 0) {
                    //first doll, delete last batch
                    for (int i = 0; i < 4; i++){
                        if (doll_list[i] != null) {
                            Destroy(doll_list[i]);
                            doll_list[i] = null;
                        }
                    }
                }

                if (pin_name != 0) {
                    spawn_doll(pin_num, pin_name);
                }

            }
            catch (System.Exception)
            {

            }
        }
    }


    void spawn_doll(int pin_num, int pin_name){

        Vector3 position = new Vector3(Random.Range(-300.0F, 300.0F), 100, Random.Range(-150.0F, 200.0F));//position
        GameObject doll_clone = (GameObject) Instantiate(doll_prefab, position, Quaternion.identity);

        GameObject body = doll_clone.transform.GetChild(0).gameObject;
        GameObject hands = doll_clone.transform.GetChild(1).gameObject;
        GameObject head = doll_clone.transform.GetChild(2).GetChild(0).gameObject;

        Renderer body_render = body.GetComponent<Renderer>();
        Renderer hands_render = hands.GetComponent<Renderer>();
        Renderer head_render = head.GetComponent<Renderer>();


        if (pin_name == 1) {
            body_render.material.color = Color.red;
            hands_render.material.color = Color.red;
            head_render.material.color = Color.red;
            doll_clone.transform.localScale = new Vector3(0.9F,0.9F,0.9F);

        } else if (pin_name == 2) {
            body_render.material.color = Color.yellow;
            hands_render.material.color = Color.yellow;
            head_render.material.color = Color.yellow;
            doll_clone.transform.localScale = new Vector3(0.92F,0.92F,0.92F);

        } else if (pin_name == 3) {
            body_render.material.color = Color.white;
            hands_render.material.color = Color.white;
            head_render.material.color = Color.white;
            doll_clone.transform.localScale = new Vector3(0.94F,0.94F,0.94F);

        } else if (pin_name == 4) {
            body_render.material.color = Color.blue;
            hands_render.material.color = Color.blue;
            head_render.material.color = Color.blue;
            doll_clone.transform.localScale = new Vector3(0.96F,0.96F,0.96F);

        } else {
            body_render.material.color = Color.black;
            hands_render.material.color = Color.black;
            head_render.material.color = Color.black;
            doll_clone.transform.localScale = new Vector3(1F,1F,1F);
        }

        doll_list[pin_num] = doll_clone;
    }
}

doll_movement:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class doll_movement : MonoBehaviour
{   
    private GameObject head;
    private GameObject hands;
    private GameObject body;
    private GameObject head_axis;
    private GameObject neck_axis;

    public float xMax;
    public float zMax;
    public float xMin;
    public float zMin;

    public float moveSpeed = 2;
    public float turnSpeed = 45;
    
    private Vector3 position = Vector3.zero;

    private float timer;
    private float timer_limit;

    private int move_bool;

    void Start()
    {
        body  = transform.GetChild(0).gameObject;
        hands  = transform.GetChild(1).gameObject;
        head_axis  = transform.GetChild(2).gameObject;
        head  = transform.GetChild(2).GetChild(0).gameObject;
        neck_axis  = transform.GetChild(3).gameObject;

        timer_limit = Random.Range(1.0f, 10.0f);
        move_bool = Random.Range(0, 4);

        RandomizePosition();

    }

    void Update()
    {
        timer += Time.deltaTime;
        
        if (transform.localPosition.x > xMax) {
            RandomizePosition();
            timer = 0.0f;
            timer_limit = Random.Range(1.0f, 10.0f);
            move_bool = Random.Range(0, 4);
        }
        if (transform.localPosition.x < xMin) {
            RandomizePosition();
            timer = 0.0f;
            timer_limit = Random.Range(1.0f, 10.0f);
            move_bool = Random.Range(0, 4);
        }
        if (transform.localPosition.z > zMax) {
            RandomizePosition();
            timer = 0.0f;
            timer_limit = Random.Range(1.0f, 10.0f);
            move_bool = Random.Range(0, 4);
        }
        if (transform.localPosition.z < zMin) {
            RandomizePosition();
            timer = 0.0f;
            timer_limit = Random.Range(1.0f, 10.0f);
            move_bool = Random.Range(0, 4);
        }

        if (timer > timer_limit) {
            RandomizePosition();
            timer = 0.0f;
            timer_limit = Random.Range(1.0f, 10.0f);
            move_bool = Random.Range(0, 4);      
        }

        if (move_bool != 3) {
            MoveGameObject();
        }

    }

    private void RandomizePosition()
    {
        position = new Vector3(Random.Range(-200, 200),transform.position.y, Random.Range(-200, 200));
    }

    private void MoveGameObject ()
    {
        Vector3 angle = position - transform.position;
        if(angle.y != 0)angle.y = 0;
        if(position.y != transform.position.y)position.y = transform.position.y;
        if(transform.position != Vector3.MoveTowards(transform.position, position, moveSpeed * Time.deltaTime))
        {
            transform.position = Vector3.MoveTowards(transform.position, position, moveSpeed * Time.deltaTime);
            transform.rotation = Quaternion.RotateTowards(transform.rotation,Quaternion.LookRotation(angle),turnSpeed * Time.deltaTime);
        }
    }

}

Resources used:

  • https://www.circuitbasics.com/arduino-ohm-meter/
  • https://www.alanzucconi.com/2015/10/07/how-to-integrate-arduino-with-unity/
  • https://answers.unity.com/questions/336663/random-movement-staying-in-an-area.html
  • https://answers.unity.com/questions/1456945/how-to-make-a-gameobject-move-randomly-withing-a-b.html
]]>
Arrows Ideation https://courses.ideate.cmu.edu/62-362/f2020/arrows-ideation/ Fri, 04 Dec 2020 06:09:33 +0000 https://courses.ideate.cmu.edu/62-362/f2020/?p=10508 Idea 1: Expansion to the Vitals Alarm (Project 2)

The Vitals Alarm that I made for Project 2 has been working well thus far. Yes, the sensors are finnicky, but I expected that and have basically figured out how to work around it. One thing I did not implement that I had intended to was long-term data logging (to an SD card). If I end up going through with this idea, then the plan is to add that, along with a simple display that shows a graph of your pulse, average BPM, and galvanic skin response (GSR). That’s a little more readable than just showing the numerical value, which is constantly fluctuating for the GSR sensor.

A readout of the numerical value from the GSR will still remain visible somewhere (probably right where it is now, on the character LCD). The raw IR value reported by the pulse oximeter (used for determining heart rate over a period of time by measuring fluctuations in that value caused by your pulse) will be made visible on the character LCD, along with a graph of my pulse and average BPM. These additions not only bring additional functionality and readability to the device, but they also expose some of the underlying mechanisms and calculations that lead to the final output(s).

The device will remain right where it is now, on my nightstand in my bedroom in Pittsburgh. Mode of interaction will remain basically the same. With that said, here’s the (slightly modified) flow of information.

Materials are probably the same as for Project 2 (Vitals Alarm), but with the addition of a microSD reader and graphical LCD. If I run into memory issues with the Uno, then an Arduino Due or Mega will also be added to this list.

UPDATE 11/30/2020, Idea 2

Upon meeting with HB and RZach on Monday 11/30, they teased an idea out of me that will most likely be far more enriching. The above idea is more like a technical completion of Project 2.

This idea, on the other hand, deals with trees in a forest and how they communicate with each other. Even between different species, trees can communicate with other trees in a forest, letting them know about their current well-being (or lack thereof if, for instance, one caught on fire). They do this to help each other survive. This idea takes a look at the concept of a network of trees and aims to create a network of a similar nature.

In this network, there will be no less than 4 nodes–three PCs and myself. The central component will be IDeATe’s MQTT server (developed in large part by Professor Garth Zeglin). The PCs will report their own vitals–CPU and GPU die temps, current utilization/load, last time they “went to sleep” and for how long they “slept.” My gateway into this network will be a modified version of the device that I made for Project 2. Using it (connected to one of the PCs so that it can communicate with the MQTT server), I will report my own vitals–Galvanic Skin Response (GSR), heart-rate, the hour I went to sleep the previous night, and for how long I slept.

All nodes in the network will be subscribed to the MQTT server so that they can know the vitals of everyone involved. The PCs will “talk” to each other or otherwise react based on how any one may be doing at a given point. For instance, if one of my PCs is under heavy load, but temperatures are in-check, then another PC may send a message of encouragement; i.e., “You’re doing great, keep it up!” On the other hand, if one is under heavy load and its temps are getting high, then any PCs near it will stop their workloads if they have them (to stop adding heat to the room) and send a different sort of message; i.e., “Finish up, but consider taking a break. I’ve stopped so that I’m not adding any more heat.” In yet another case, if the load for a system is low, but the temperatures are high, then that indicates a problem. This will cause the other PCs in the network to “sound the alarm” and notify me that the PC is having serious problems.

That’s a brief overview of PC-to-PC interactions. There will also be PC-to-human interactions based on the human node’s vitals. For instance, if my GSR reading is really low or my heart rate is really high (indicating that I am stressed), then one or more of the PCs will send a message to the Arduino (which will, in turn, be sent to me) saying something along the lines of “You’re stressed, take a break dude…” There will also be encouraging messages if I do something that would be beneficial to my own well-being, like going to sleep at a reasonable hour and sleeping for an adequate number of hours.

Essentially, I’m integrating myself into the machine.

With all of that said, the flow of information may look something like this. For simplicity sake, I’m only showing one PC in the network.

I already have all of the materials I would need to make this. Time to get to it.

]]>