Tom Minnich
Published © GPL3+

Medicine Reminder Raspberry Pi, Alexa Voice and Hexiwear

Helpful reminders to take medicines on time, make medicines more effective, using Raspberry PI, Alexa Voice and Hexiwearl!

AdvancedFull instructions provided20 hours3,560
Medicine Reminder Raspberry Pi, Alexa Voice and Hexiwear

Things used in this project

Hardware components

Raspberry Pi 3 Model B
Raspberry Pi 3 Model B
×1
LED (generic)
LED (generic)
×7
Resistor 221 ohm
Resistor 221 ohm
×7
pill organizer box
×1
Jumper wires (generic)
Jumper wires (generic)
×1
Breadboard (generic)
Breadboard (generic)
×1
Raspberry Pi T - cobbler board
×1
Hexiwear
NXP Hexiwear
×1

Software apps and online services

Alexa Skills Kit
Amazon Alexa Alexa Skills Kit
AWS Lambda
Amazon Web Services AWS Lambda
AWS IoT
Amazon Web Services AWS IoT

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)

Story

Read more

Custom parts and enclosures

Click Storage Box

An Autodesk Fusion 360 design for a storage box for up to 12 Click Modules.
It is in two pieces that snap together to make a hinge. The lid of the storage case is designed to hold the Hexiwear Docking station and keep it from sliding around on your desk.

Schematics

My Medicine Reminder Schematic

LEDs to light up 7 day pill planner box

Code

medminder.py

Python
#!/usr/bin/python

"""python  Medicine Reminder to be controlled by Amazon Alexa"""

import paho.mqtt.client as mqtt
import json, time, sys, ssl
import logging, logging.handlers
import RPi.GPIO as GPIO  

time_window_size = 5 #duration of the reminder in minutes
schedule = [1080,1142,1200] #list of scheduled medicine reminders
#schedule is kept in minutes past midnight


sunday = 24
monday = 17
tuesday = 21
wednesday = 11
thursday = 10
friday = 15
saturday = 13

GPIO.setmode(GPIO.BCM)  

GPIO.setup(sunday, GPIO.OUT)
GPIO.setup(monday, GPIO.OUT)
GPIO.setup(tuesday, GPIO.OUT)
GPIO.setup(wednesday, GPIO.OUT)
GPIO.setup(thursday, GPIO.OUT)
GPIO.setup(friday, GPIO.OUT)
GPIO.setup(saturday, GPIO.OUT)
GPIO.output(sunday, GPIO.HIGH)
GPIO.output(monday, GPIO.HIGH)
GPIO.output(tuesday, GPIO.HIGH)
GPIO.output(wednesday, GPIO.HIGH)
GPIO.output(thursday, GPIO.HIGH)
GPIO.output(friday, GPIO.HIGH)
GPIO.output(saturday, GPIO.HIGH)

cert_path = "/home/pi/Desktop/pi_pi/certs/"
host = "avqjd231ms9qb.iot.us-east-1.amazonaws.com"
topic = "$aws/things/tom_thing/shadow/update"
root_cert = cert_path + "rootCA.key"
cert_file = cert_path + "393e2d8773-certificate.pem.crt"
key_file = cert_path + "393e2d8773-private.pem.key"

globalmessage = ""  # to send status back to MQTT
isConnected = False
logger = logging.getLogger('medminder')

def do_setreminder(data):  #   {"name":"SetReminder","slots":{"Time":{"name":"Time","value":"time_value"}}}
    time_str = str(data["slots"]["Time"]["value"])
    global globalmessage
    globalmessage = "executing SetReminder",time_str
    #add the time to the schedule list
    time_lst = time_str.split(":")
    if len(time_lst) != 2:
        logger.info("SET TIME could not work this string ")
        return
    logger.info("SET TIME = " + time_str)
    time_hrs = int(time_lst[0])
    time_mins = int(time_lst[1])
    total_mins = time_mins + 60*time_hrs
    added = False
    if total_mins in schedule:
        #do not need to add it to the schedule list if it exists in the schedule list already!!!
        logger.info("already exists in schedule! ")
    else:
        #store the set reminder time in sorted order in the schedule list
        loop_count = 0
        while loop_count < len(schedule):
            if total_mins < schedule[loop_count]:
                schedule.insert(loop_count,total_mins)
                added = True
                break
            loop_count += 1
        if added == False:
            schedule.append(total_mins)        
        logger.info(schedule)
        

def do_clearreminder(data):  #   {"name":"ClearReminder","slots":{"Time":{"name":"Time","value":"time_value"}}}
    time_str = str(data["slots"]["Time"]["value"])

    global globalmessage
    globalmessage = "executing ClearReminder",time_str
    #clear the time to the schedule list
    time_lst = time_str.split(":")
    if len(time_lst) != 2:
        logger.info("CLEAR TIME could not work this string ")
        return
    logger.info("CLEAR TIME = " + time_str)
    time_hrs = int(time_lst[0])
    time_mins = int(time_lst[1])
    total_mins = time_mins + 60*time_hrs
    if total_mins in schedule:
        #do not need to add it to the schedule list if it exists in the schedule list already!!!
        logger.info("Removed from list.")
    
def toggle(gpio,cmd):
    logger.info("GPIO: " + str(gpio) + "CMD: " + str(cmd))
    GPIO.output(gpio, cmd)
        
# The callback for when the client receives a CONNACK response from the server.
def on_connect(client, userdata, flags, rc):
    isConnected = True
    logger.info("connected with result code " + str(rc))
    # Subscribing in on_connect() means that if we lose the connection and      
    # reconnect then subscriptions will be renewed.    
    client.subscribe(topic)

# The callback for when a PUBLISH message is received from the server.
def on_message(client, userdata, msg):
    logger.info("msg received, payload = " + str(msg.payload))
    data = json.loads(str(msg.payload))

    if "name" in data:
        if data["name"] == "SetReminder":
            do_setreminder(data)
        elif data["name"] == "ClearReminder":
            do_clearreminder(data)
        
def on_log(client, userdata, level, buf):
    logger.debug(buf)

def logger_init():
    logger.setLevel(logging.DEBUG)
    log_file_size = 1024 * 1024 * 1  # 1 MB
    formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(process)d - %(name)s : %(message)s')
    fh = logging.handlers.RotatingFileHandler('/home/pi/Desktop/pi_pi/logs/new_medminder.log', maxBytes=log_file_size, backupCount=5)
    fh.setFormatter(formatter)
    sh = logging.StreamHandler(sys.stdout)
    sh.setFormatter(formatter)
    logger.addHandler(fh)
    logger.addHandler(sh)
    logger.info('******************************************')
    logger.info('Starting up...')

logger_init()

client = mqtt.Client(client_id="medminder.py")
client.on_connect = on_connect
client.on_message = on_message
client.on_log = on_log
client.tls_set(root_cert,
               certfile = cert_file,
               keyfile = key_file,
               cert_reqs=ssl.CERT_REQUIRED,
               tls_version=ssl.PROTOCOL_TLSv1_2,
               ciphers=None)

logger.info('connecting to mqtt broker')
client.connect(host, 8883, 60)

run = True

was_printed = False
off_was_printed = True
one_time_only = False
checked_schedule = False

try:
    while run:
        client.loop()
          
        time.sleep(1)
        dt = list(time.localtime())
        hour = dt[3]
        minute = dt[4]
        second = dt[5]
        weekday = dt[6] # Monday is 0

        if second < 10: #check schedule once a minute only
            if one_time_only == False:
                one_time_only = True
                checked_schedule = False
        else:
            one_time_only = 0        

        cur_time_mins = minute + 60 * hour # current time in minutes past midnight
        
        if checked_schedule == False:
            if was_printed == False:
                checked_schedule = True
                schedule_loop = 0
                while schedule_loop < len(schedule):
                    schedule_time_window = schedule[schedule_loop] + time_window_size
                    if schedule_time_window > (59 + 23*60):
                        schedule_time_window = (59 + 23*60)
                    if cur_time_mins < schedule_time_window:
                        if schedule[schedule_loop] <= cur_time_mins:
                            logger.info("MEDICINE REMINDER!!!")
                            was_printed = True;
                            turn_off_mins = schedule[schedule_loop] + time_window_size
                            off_was_printed = False
                            if turn_off_mins > (59 + 23*60): # if turn off past midnight
                                turn_off_mins = (59 + 23*60) #   then turn off one minute before midnight
                            if weekday == 0:
                                GPIO.output(monday, GPIO.LOW)
                            elif weekday == 1:
                                GPIO.output(tuesday, GPIO.LOW)
                            elif weekday == 2:
                                GPIO.output(wednesday, GPIO.LOW)
                            elif weekday == 3:
                                GPIO.output(thursday, GPIO.LOW)
                            elif weekday == 4:
                                GPIO.output(friday, GPIO.LOW)
                            elif weekday == 5:
                                GPIO.output(saturday, GPIO.LOW)
                            elif weekday == 6:
                                GPIO.output(sunday, GPIO.LOW)
                    schedule_loop += 1
        if off_was_printed == False:
            if cur_time_mins >= turn_off_mins:
                logger.info("MEDICINE REMINDER OFF")
                off_was_printed = True
                was_printed = 0 # to prevent re-checking during the reminder period
                #that would move the reminder window and we do not want that
                if weekday == 0:
                    GPIO.output(monday, GPIO.HIGH)
                elif weekday == 1:
                    GPIO.output(tuesday, GPIO.HIGH)
                elif weekday == 2:
                    GPIO.output(wednesday, GPIO.HIGH)
                elif weekday == 3:
                    GPIO.output(thursday, GPIO.HIGH)
                elif weekday == 4:
                    GPIO.output(friday, GPIO.HIGH)
                elif weekday == 5:
                    GPIO.output(saturday, GPIO.HIGH)
                elif weekday == 6:
                    GPIO.output(sunday, GPIO.HIGH)

        try:
            mypayload = '''{
                "StatusMessage": "%s"
            }''' % (globalmessage)

            if globalmessage != "":
                client.publish(topic, mypayload)
                globalmessage = ""

        except (TypeError):
            pass
    
except KeyboardInterrupt:
    print "Bye Bye!"
    GPIO.cleanup()    

intent schema

XML
{
   "intents":[
      {
         "intent":"SetReminder",
         "slots":[
            {
               "name":"Time",
               "type":"AMAZON.TIME"
            }
         ]
      },
      {
         "intent":"ClearReminder",
         "slots":[
            {
               "name":"Time",
               "type":"AMAZON.TIME"
            }
         ]
      }
   ]
}

Sample Utterances

Plain text
Used interaction model for the My Medicine Reminder skill
SetReminder {Time}
SetReminder Set reminder for {Time}
SetReminder Set for {Time}
SetReminder Set {Time}
SetReminder Set time {Time}
ClearReminder {Time}
ClearReminder Clear reminder for {Time}
ClearReminder Clear for {Time}
ClearReminder Clear {Time}
ClearReminder Clear time {Time}

MyMedicineReminder

This is what gets zipped up and uploaded to create the Lambda function that is used with the MyMedicineReminder voice skill

Credits

Tom Minnich

Tom Minnich

19 projects • 81 followers
Embedded software guy for a long time

Comments