Manan Rai
Published © CC BY-NC-ND

Helping the Disabled and Chronic Patients battle the heat

Heat brings along challenges for the differently-abled. Let's build a bladeless fan, wireless pacemaker and communication aid to help them!

AdvancedFull instructions provided8,582
Helping the Disabled and Chronic Patients battle the heat

Things used in this project

Hardware components

Omega2
Onion Corporation Omega2
×1
Arduino UNO
Arduino UNO
×1
Expansion Dock
Onion Corporation Expansion Dock
×1
SparkFun Temperature Sensor DS18B20
×1
Computer Fan
×1
Qi 5W Transmitter Prototype Kit
IDT Qi 5W Transmitter Prototype Kit
×1
Qi 5W Receiver Prototype Kit
IDT Qi 5W Receiver Prototype Kit
×1
MSP430 Microcontroller
Texas Instruments MSP430 Microcontroller
×1
SparkFun Pulse Sensor SEN-11574
×1
Flex Resistors
×2
GSM SIM900
×1
RGB Backlight LCD - 16x2
Adafruit RGB Backlight LCD - 16x2
×1
Analog Devices AD5252 Digital Potentiometer 2-Channel 256-Position I2C Mini Module
National Control Devices Analog Devices AD5252 Digital Potentiometer 2-Channel 256-Position I2C Mini Module
×1
Resistors
×1
Transistors
×1
Capacitors
×1
Circuit Board (generic)
×1
Breadboard (generic)
Breadboard (generic)
×1

Software apps and online services

Cinema 4D
Arduino IDE
Arduino IDE
Fritzing
Autodesk EAGLE
Autodesk EAGLE
Watson
IBM Watson
BlueMix
IBM BlueMix

Hand tools and fabrication machines

Ultimaker 3D Printer
Soldering iron (generic)
Soldering iron (generic)
Hot glue gun (generic)
Hot glue gun (generic)
Screw driver set (generic)

Story

Read more

Custom parts and enclosures

Bladeless Fan Components - 3DS Format

Bladeless Fan - 3DS Format

Pacemaker in Hermetically Sealed Case

A 3-D Model of the Pacemaker in a hermetically sealed case.

Pacemaker Components

Representing how the various parts of a Pacemaker bundle up into a single, cohesive life-saving machine!

Pacemaker Components - 3DS Format

Schematics

Pacemaker Circuit - Schematic Diagram

This shows the internal circuitry of a Demand Pacemaker.

Pulse Generating Circuit with Vibration Motor

A Breadboard Representation of the Pulse Generating Circuit using a Vibration Motor as Load.

Pulse Generating Circuit PCB

Pacemaker PCB

Pacemaker PCB Gerber Files

Code

BladelessFan.py

Python
The file containing main. This is the code I wrote to operate the bladeless fan depending upon the outside temperature as well as the body temperature of the user.
import os
import sys
import json
import oneWire
import geojson
import dateutil.parser
import datetime
import serial
import pytz
import urllib
from temperatureSensor import TemperatureSensor
from omegaMotors import OmegaPwm, hBridgeMotor

# PWM channels for case fan, plus additional channels for H-bridge operation
H_BRIDGE_1A_CHANNEL = 1
H_BRIDGE_2A_CHANNEL = 2
FAN_PWM_CHANNEL = 0

oneWireGpio = 3 # mark the sensor GPIO

tempMax = 0
tempMin = 0

dutyMax = 0
dutyMin = 0
dutyStep = 0

def calcFanSpeed (temp):

    # restricting the temperature within the operating range.
    if (temp > tempMax):
        temp = tempMax
    if (temp < tempMin):
        return 0

    tempDelta = temp - tempMin


    duty = dutyMin + (dutyStep * float(tempDelta))

    return duty

if __name__ == '__main__':
    dirName = os.path.dirname(os.path.abspath(__file__))
    with open( '/'.join([dirName, 'config.json']) ) as f:
        config = json.load(f)

    dutyMin = float(config['dutyMin'])
    dutyMax = float(config['dutyMax'])

    tempMin = float(config['tempMin'])
    tempMax = float(config['tempMax'])

    dutyStep = (dutyMax - dutyMin)/(tempMax - tempMin )

    fan = OmegaPwm(FAN_PWM_CHANNEL)
    fan._setFrequency(config['frequency'])


    if not oneWire.setupOneWire(str(oneWireGpio)):
        print "Kernel module could not be inserted. Please reboot and try again."


    #~~~ SENSOR SETUP BEGIN

    sensorAddress = oneWire.scanOneAddress()

	# instantiate the temperature sensor object
    sensor = TemperatureSensor("oneWire", { "address": sensorAddress, "gpio": oneWireGpio })
    success = sensor.ready

    #~~~ SENSOR SETUP END

    if not success:
        print "Sensor was not set up correctly. Please make sure that your sensor is firmly connected to the GPIO specified above and try again."
        sys.exit(0)
    else:
        # read temp value
        temp1 = sensor.readValue() # body temperature

        dock = serial.Serial(port="/dev/ttyS1", baudrate=57600)
        
        now = datetime.datetime.now(pytz.timezone('US/Pacific'))
        print datetime.datetime.strftime(now, "%x")
        #dock.write(datetime.datetime.strftime(now, "%x") + "   |   ")
        
        weatherUrl = "https://api.weather.gov/points/37.4241,-122.1661/forecast"
        response = urllib.urlopen(weatherUrl)
        forecast = geojson.loads(response.read())
        
        summary = forecast["properties"]["periods"]
        for period in summary:
            startTime = dateutil.parser.parse(period['startTime'])
            endTime = dateutil.parser.parse(period['endTime'])
            if (datetime.datetime.now(pytz.utc) < endTime) and (datetime.datetime.now(pytz.utc) > startTime):
                print str(period["detailedForecast"]) + "\n"
                #dock.write(str(period['detailedForecast']) + "\n")
                temp2 = period["temperature"] # outside temperature
                print temp2
                
        arduino = serial.Serial(port="/dev/tty.usbserial", baudrate=9600)
        
        # respond if glovvy sends an input to alter fan speed
        while true:
            choice = arduino.readline()
            if temp1 > temp2:
                duty = calcFanSpeed(temp1 - temp2)
            
            if choice == "Increase":
                duty += 1
                fan.setDutyCycle(duty)
            elif choice == "Decrease":
                duty -= 1
                fan.setDutyCycle(duty)
          

Glovvy.ino

C/C++
This is the code that runs on the Arduino and reads user input from the flex sensors.
#include <SoftwareSerial.h>
#include <LiquidCrystal.h>
SoftwareSerial SIM900(2, 3);  //rx,tx

// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(7, 8, 9, 10, 11, 12);
int timesTosend=1;
int count=0;

void setup() {
  analogReference(DEFAULT);

  lcd.begin(16, 2);
  
  // Print a message to the LCD.
  lcd.print("Manan’s Smart Glove Project");
  lcd.setCursor(0, 1);
  Serial.begin(9600); //All Serial. commands are for testing purpose only for the developer like you and me
  
  Serial.println("Hello ");
  SIM900.begin(9600);
  SIM900.println("AT+CMGF=1"); //set GSM to text mode
}

void loop() {
  // the  main code is put here , to run repeatedly:
  Serial.print(analogRead(A0));
  Serial.print(" ");
  Serial.print(analogRead(A2));
  Serial.println(" ");
  delay(300);

  if((analogRead(A0) >= 200 && analogRead(A0) <= 240) && (analogRead(A2) >= 80 && analogRead(A2) <= 120))
  {
    Serial.println("Food");
  
       lcd.setCursor(0, 1);
    lcd.print("Food       ");
     SIM900.begin(9600);
  SIM900.println("AT+CMGF=1"); //set GSM to text mode
     while(count<timesTosend){
  delay(1500);
  SIM900.print("AT+CMGS=\"");
  SIM900.print("9810115814");
  SIM900.println("\"");
  while (SIM900.read()!='>');
  {
    Serial.println("msg sent food");
  SIM900.print("Food Requirement Alert!");  //SMS body
  delay(500);
  SIM900.write(0x1A);  // sends ctrl+z end of message
      SIM900.write(0x0D);  // Carriage Return in Hex
  SIM900.write(0x0A);  // Line feed in Hex
  
  //The 0D0A pair of characters is the signal for the end of a line and beginning of another.
  delay(5000);
  }
  count++;
  } 
  }
  else if((analogRead(A2) >= 20 && analogRead(A2) <= 70) && (analogRead(A0) >= 160 && analogRead(A0) <= 190))
  {
    Serial.println("Decrease");
    lcd.setCursor(0, 1);
    lcd.print("Decrease Speed   ");
    SIM900.begin(9600);
    SIM900.println("AT+CMGF=1"); //set GSM to text mode
    while(count<timesTosend){
        delay(1500);
        SIM900.print("AT+CMGS=\"");
        SIM900.print("9810115814"); // PUT THE ATTENDANTS PHONE NUMBER HERE 
        SIM900.println("\"");
        
        while (SIM900.read()!='>');
        {
            Serial.println("msg sent medicine");
            SIM900.print("Medicine Requirement Alert!");  //SMS body
  
            delay(500);
            SIM900.write(0x1A);  // sends ctrl+z end of message
            SIM900.write(0x0D);  // Carriage Return in Hex
            SIM900.write(0x0A);  // Line feed in Hex
  
        //The 0D0A pair of characters is the signal for the end of a line and beginning of another.
            delay(5000);
        }
        count++;
    } 
  }
  
  else if((analogRead(A2) >= 80 && analogRead(A2) <= 120) && (analogRead(A0) >= 160 && analogRead(A0) <= 190))
  {
    Serial.println("emergency ");
    lcd.setCursor(0, 1);
    lcd.print("Emergency   ");
    SIM900.begin(9600);
    SIM900.println("AT+CMGF=1"); //set GSM to text mode
    while(count<timesTosend){
        delay(1500);
        SIM900.print("AT+CMGS=\"");
        SIM900.print("9810115814");
        SIM900.println("\"");
        while (SIM900.read()!='>');
        {
            Serial.println("msg sent emergency");
            SIM900.print("Emergency Alert!");  //SMS body
            
            delay(500);
            SIM900.write(0x1A);  // sends ctrl+z end of message
            SIM900.write(0x0D);  // Carriage Return in Hex
            SIM900.write(0x0A);  // Line feed in Hex
            
        //The 0D0A pair of characters is the signal for the end of a line and beginning of another.
            delay(5000);
        }
        count++;
    } 
  }
  
  else if((analogRead(A0) >= 200 && analogRead(A0) <= 240) && (analogRead(A2) >= 20 && analogRead(A2) <= 70))
  {
    Serial.println("Increase");
    lcd.setCursor(0, 1);
    lcd.print("Increase Speed     ");	
    count = 0;
  }
}

omegaMotors.py

Python
Source: https://github.com/OnionIoT/iot-smart-fan
from OmegaExpansion import pwmExp

__version__ = "0.1"

SERVO_FREQUENCY = 50
SERVO_MIN_PULSE = 600  # 600us 	= 0.6ms
SERVO_MAX_PULSE = 2400  # 2400us 	= 2.4ms

H_BRIDGE_MOTOR_FORWARD = 0
H_BRIDGE_MOTOR_REVERSE = 1

class OmegaPwm:
    """Base class for PWM signal"""

    def __init__(self, channel):
        self.channel = channel
        self.frequency = SERVO_FREQUENCY

        # check that pwm-exp has been initialized
        bInit = pwmExp.checkInit()

        if (bInit == 0):
            # initialize the expansion
            ret = pwmExp.driverInit()
            if (ret != 0):
                print 'ERROR: pwm-exp init not successful!'

            # set to default frequency
            self._setFrequency(self.frequency)

    def _setFrequency(self, freq):
        """Set frequency of pwm-exp oscillator"""
        self.frequency = freq
        ret = pwmExp.setFrequency(freq);
        if (ret != 0):
            print 'ERROR: pwm-exp setFrequency not successful!'

        return ret

    def getFrequency(self):
        """Get frequency of pwm-exp oscillator"""
        return self.frequency

    def setDutyCycle(self, duty):
        """Set duty cycle for pwm channel"""
        ret = pwmExp.setupDriver(self.channel, duty, 0)
        if (ret != 0):
            print 'ERROR: pwm-exp setupDriver not successful!'

        return ret


class DigitalPin:
    """Class that uses PWM signals as 0 or 1"""

    def __init__(self, channel):
        # initialize a pwm channel
        self.channel = channel
        self.pwmDriver = OmegaPwm(self.channel)

    def set(self, value):
        """Set the PWM signal to either OFF (0%) or ON (100%)"""
        if (value == 0):
            duty = 0
        else:
            duty = 100

        ret = self.pwmDriver.setDutyCycle(duty)
        return ret

    def turnOff(self):
        """Set the PWM signal to OFF (0%)"""
        ret = self.set(0)
        return ret

    def turnOn(self):
        """Set the PWM signal to ON (100%)"""
        ret = self.set(100)
        return ret


class Servo:
    """Class that uses PWM signals to drive a servo"""

    def __init__(self, channel, minPulse, maxPulse):
        # initialize a pwm channel
        self.channel = channel
        self.pwmDriver = OmegaPwm(self.channel)

        # note the min and max pulses (in microseconds)
        self.minPulse = minPulse
        self.maxPulse = maxPulse

        # calculate the total range
        self.range = self.maxPulse - self.minPulse

        # calculate the us / degree
        self.step = self.range / float(180)

        # calculate the period (in us)
        self.period = (1000000 / self.pwmDriver.getFrequency())

        # initialize the min and max angles
        self.minAngle = 0
        self.maxAngle = 180

    def getSettings(self):
        """Return an object with all settings"""
        settings = {}
        settings['channel'] = self.channel
        settings['minPulse'] = self.minPulse
        settings['maxPulse'] = self.maxPulse

        settings['range'] = self.range
        settings['step'] = self.step
        settings['period'] = self.period

        settings['minAngle'] = self.minAngle
        settings['maxAngle'] = self.maxAngle

        return settings

    def setupMinAngle(self, angle):
        """Program a minimum angle"""
        self.minAngle = angle

    def setupMaxAngle(self, angle):
        """Program a maximum angle"""
        self.maxAngle = angle

    def setAngle(self, angle):
        """Move the servo to the specified angle"""
        # check against the minimum and maximium angles
        if angle < self.minAngle:
            angle = self.minAngle
        elif angle > self.maxAngle:
            angle = self.maxAngle

        # calculate pulse width for this angle
        pulseWidth = angle * self.step + self.minPulse

        # find the duty cycle percentage of the pulse width
        duty = (pulseWidth * 100) / float(self.period)

        # program the duty cycle
        ret = self.pwmDriver.setDutyCycle(duty)
        return ret


class hBridgeMotor:
    """Class that two digital signals and a pwm signal to control an h-bridge"""

    def __init__(self, pwmChannel, fwdChannel, revChannel):
        # note the channels
        self.pwmChannel = pwmChannel
        self.fwdChannel = fwdChannel
        self.revChannel = revChannel

        # setup the objects
        self.pwmDriver = OmegaPwm(self.pwmChannel)
        self.pwmDriver.setDutyCycle(0)
        self.fwdDriver = DigitalPin(self.fwdChannel)
        self.fwdDriver.turnOff()
        self.revDriver = DigitalPin(self.revChannel)
        self.revDriver.turnOff()

        # setup the limitations
        self.minDuty = 0
        self.maxDuty = 100

    def setupMinDuty(self, duty):
        """Set the minimum allowed duty cycle for pwm"""
        self.minDuty = duty

    def setupMaxDuty(self, duty):
        """Set the maximum allowed duty cycle for pwm"""
        self.maxDuty = duty

    def reset(self):
        """Set the PWM to 0%, disable both h-bridge controls"""
        ret = self.pwmDriver.setDutyCycle(0)
        ret |= self.fwdDriver.turnOff()
        ret |= self.revDriver.turnOff()

        return ret

    def drive(self, direction, duty):
        """Set the PWM to the specified duty, and in the specified direction"""
        ret = 0

        # 0 - forward, 1 - reverse
        if (direction == H_BRIDGE_MOTOR_FORWARD):
            self.revDriver.turnOff()
            self.fwdDriver.turnOn()
        elif (direction == H_BRIDGE_MOTOR_REVERSE):
            self.fwdDriver.turnOff()
            self.revDriver.turnOn()
        else:
            ret = -1

        if (ret == 0):
            # check against the minimum and maximium pwm
            if duty < self.minDuty:
                duty = self.minDuty
            elif duty > self.maxDuty:
                duty = self.maxDuty

            # program the duty cycle
            ret = self.pwmDriver.setDutyCycle(duty)
        return ret

    def spinForward(self, duty):
        ret = self.drive(H_BRIDGE_MOTOR_FORWARD, duty)
        return ret

    def spinReverse(self, duty):
        ret = self.drive(H_BRIDGE_MOTOR_REVERSE, duty)
        return ret

oneWire.py

Python
Source: https://github.com/OnionIoT/iot-smart-fan
import os
import subprocess
import time

# specify delay duration to be used in the program
setupDelay = 3

# variables to hold filesystem paths
oneWireDir = "/sys/devices/w1_bus_master1"
paths = {
    "slaveCount": oneWireDir + "/w1_master_slave_count",
    "slaves": oneWireDir + "/w1_master_slaves"
}

## a bunch of functions to be used by the OneWire class
# insert the 1-Wire kernel module
# it's also called a "module", but it's actually software for the Omega's firmware!
def insertKernelModule(gpio):
    argBus = "bus0=0," + gpio + ",0"
    subprocess.call(["insmod", "w1-gpio-custom", argBus])

# check the filesystem to see if 1-Wire is properly setup
def checkFilesystem():
    return os.path.isdir(oneWireDir)

# function to setup the 1-Wire bus
def setupOneWire(gpio):
    # check and retry up to 2 times if the 1-Wire bus has not been set up
    for i in range (2):
        if checkFilesystem():
            return True # exits if the bus is setup
            # no else statement is needed after this return statement

        # tries to insert the module if it's not setup
        insertKernelModule(gpio)
        # wait for a bit, then check again
        time.sleep(setupDelay)
    else:
        # could not set up 1-Wire on the gpio
        return False

# check that the kernel is detecting slaves
def checkSlaves():
    with open(paths["slaveCount"]) as slaveCountFile:
        slaveCount = slaveCountFile.read().split("\n")[0]

    if slaveCount == "0":
        # slaves not detected by kernel
        return False
    return True

# check if a given address is registered on the bus
def checkRegistered(address):
    slaveList = scanAddresses()
    registered = False
    for line in slaveList:
        if address in line:
            registered = True
    return registered

# scan addresses of all connected 1-w devices
def scanAddresses():
    if not checkFilesystem():
        return False

    with open(paths["slaves"]) as slaveListFile:
        slaveList = slaveListFile.read().split("\n")
        # last element is an empty string due to the split
        del slaveList[-1]
    return slaveList

# use to get the address of a single connected device
def scanOneAddress():
    addresses = scanAddresses()
    return addresses[0]

# class definition for one wire devices
class OneWire:
    def __init__(self, address, gpio=19):      # use gpio 19 by default if not specified
        self.gpio = str(gpio)
        self.address = str(address)
        self.slaveFilePath = oneWireDir + "/" + self.address + "/" + "w1_slave"
        self.setupComplete = self.__prepare()

    # prepare the object
    def __prepare(self):
        # check if the system file exists
        # if not, set it up, then check one more time
        if not setupOneWire(self.gpio):
            print "Could not set up 1-Wire on GPIO " + self.gpio
            return False

        # check if the kernel is recognizing slaves
        if not checkSlaves():
            print "Kernel is not recognizing slaves."
            return False

        # check if this instance's device is properly registered
        if not checkRegistered(self.address):
            # device is not recognized by the kernel
            print "Device is not registered on the bus."
            return False

        # the device has been properly set up
        return True

	# function to read data from the sensor
    def readDevice(self):
        # read from the system file
        with open(self.slaveFilePath) as slave:
            message = slave.read().split("\n")
        	# return an array of each line printed to the terminal
        return message

temperatureSensor.py

Python
Source: https://github.com/OnionIoT/iot-smart-fan
from oneWire import OneWire

# main class definition
class TemperatureSensor:
    def __init__(self, interface, args):
        self.supportedInterfaces = ["oneWire"]
        self.interface = interface
        self.ready = False

        # if specified interface not supported
        if self.interface not in self.supportedInterfaces:
            print "Unsupported interface."
            self.listInterfaces()
            return

        # set up a driver based on the interface type
        # you can extend this class by adding more drivers! (eg. serial, I2C, etc)
        if self.interface == "oneWire":
            self.driver = OneWire(args.get("address"), args.get("gpio", None))
            # signal ready status
            self.ready = self.driver.setupComplete
            # match the return value to
            self.readValue = self.__readOneWire;

    def listInterfaces(self):
        print "The supported interfaces are:"
        for interface in self.supportedInterfaces:
            print interface

    # read one-wire devices
    def __readOneWire(self):
        # device typically prints 2 lines, the 2nd line has the temperature sensor at the end
        # eg. a6 01 4b 46 7f ff 0c 10 5c t=26375
        rawValue = self.driver.readDevice()

        # grab the 2nd line, then read the last entry in the line, then get everything after the "=" sign
        value = rawValue[1].split()[-1].split("=")[1]

        # convert value from string to number
        value = int(value)

        # DS18B20 outputs in 1/1000ths of a degree C, so convert to standard units
        value /= 1000.0
        return value

    # add more __read() functions for different interface types later!

config.json

JSON
Source: https://github.com/OnionIoT/iot-smart-fan
{
    "tempMax" : "40",
    "tempMin" : "18",
    "dutyMin" : "60",
    "dutyMax" : "100",
    "frequency" : "1000",
    "fanType" : "case"
}

Credits

Manan Rai

Manan Rai

0 projects • 10 followers

Comments