A lot of people have trouble taking their medications, especially older adults. It is precisely from the latter that the idea of the PortaPillole was born. A device equipped with 3 programmable slots. Each slot can be configured with the name of the medicine and the time to take it. At the right time, the slot is opened thanks to a servo, and a buzzer plays the sound of a timer to alert the user. Everything is programmable via an HC-05 Bluetooth module controlled by a Blues Swam. Programming can be done via an Android app, the data of the individual slots are saved on the microcontroller's memory, allowing it to continue to function perfectly without altering the data, even in the event of a power loss.
The HardwareTo create the device I used a Blues Swan as the brain of the PortaPillole, an HC-05 to communicate and program the Slots, an RTC to obtain the time in real time, a servo to unlock the 3 slots and a buzzer to reproduce the sound of the timer.
- Blues Swan is a low-cost embeddable STM32L4+-based microcontroller board designed to accelerate the development and deployment of battery-powered IoT solutions.
- RTC Module is a module with integrated battery that allows you to obtain both the time and date in real time. Even if the main controller loses power (in this case the Blues Swan) the device, thanks to its own battery, is able to continue measuring time dilation.
- HC-05 is a module that allows you to introduce Bluetooth connectivity to your project. Thanks to serial communication it is then possible to make it communicate with the main controller.
- Buzzer is a device that allows you to reproduce sounds and musical notes. A simple speaker can also be used.
- Servo is a simple motor with an internal regulator that allows you to control the rotation precisely. To use 2 Slots even a 180° Servo is fine, while if you want to develop a 3 Slot device you need a 360° Servo.
All components are connected to each other following this simple diagram.
As you can see, everything is powered by the Blues Swan's 3.3V. While the HC-05 is connected to the Blues Swan via the TX and RX pins which allow the two cards to communicate via the Serial port.
It is important that the RX of the HC-05 is connected to the TX of the Blues Swan while the TX of the HC-05 is connected to the RX of the Blues Swan.
The RTC is connected to the Blues Swan with the SDA and SCL pins which allows you to obtain the time in real time via I2C communication. Finally the Servo is connected to D6 and the buzzer to D5.
I used a BreadBoard and soldered the components together allowing for a tidier and more homogeneous device.
The Structure
To create the device cover I used some 3D models developed in Autodesk Tinkercad. The base has a space to insert your Buzzer and an attachment for the Servo.
Connected to the servo, the Slot structure is positioned. In my case I used a 360° Servo which allowed me to develop 3 programmable Slots and one covered one which is defined as Slot 0 (the device will use this element when it is waiting to unlock the Slots with medicines). All these elements are 3D printed using black PLA.
To program PortaPillole I used the PlatformIO environment. If desired, the Blues Swan can also be programmed via Arduino IDE or STM32CubeIDE.
To configure PlatformIO you can follow this short guide on the Blues website. The process is very simple.
Thelibraries
Wire
SPI
SoftwareSerial
EEPROM
blues/Blues Wireless Notecard@^1.5.1
adafruit/RTClib@^2.1.1
The code is based on the following libraries.
SPI is used together with RTClib to connect to the RTC module.
SoftwareSerial is instead used to communicate with the HC-05 module via Serial connectivity.
Finally, the EEPROM library allows you to save the slot data on FLASH memory, allowing you to access the saved data even after restarting the board.
All the code
This is the complete code.
//Created by Pasquale Pepe pasqualepepe887@gmail.com
#include <Arduino.h>
#include <Servo.h>
#include <SPI.h>
#include <RTCLib.h>
#include<SoftwareSerial.h>
#include <EEPROM.h>
RTC_DS3231 rtc;
Servo m1; //Use a 180 Servo for 2 SLOT or a 360 Servo for 3 Slot
// HC-05 BLUES TX-> RX RX-> TX VCC -> 3.3V
int i=0;
bool edit_mode;
char inputString[20];
char back_date;
const char targetString_A[]= "get/date/A";
const char targetString_B[]= "get/date/B";
const char targetString_C[]= "get/date/C";
String a_search= "S/A/";
String b_search= "S/B/";
String c_search= "S/C/";
int stringIndex=0;
String inputString_set="";
String name_ext = "";
String time_ext = "";
bool one_time;
void sound(){
for (int k=0; k<=10;k++){
tone(D5,1000,200);
delay(500);
noTone(D5);
delay(500);
}
}
void save_memory(String data,int address){
for (int j=0; j<data.length();j++){
EEPROM.write(address+j,data[j]);
}
EEPROM.write(address+data.length(),'\0');
}
String read_memory(int address,int lenght_str){
char dest[lenght_str+1];
for (int j=0; j<lenght_str;j++){
dest[j]=EEPROM.read(address+j);
if (dest[j] =='\0'){
break;
}
}
dest[lenght_str]= '\0';
return String(dest);
}
String get_date(char slot, char date_type){ //date_type or T=time or N=name
int address_buffer;
String date_buffer= "";
String return_date= "";
if (slot == 'A'){
address_buffer= 0;
date_buffer= read_memory(address_buffer,30);
if (date_type == 'N'){
return_date= date_buffer.substring(1,date_buffer.indexOf('/')-1);
}else if (date_type == 'T'){
return_date= date_buffer.substring(date_buffer.indexOf('/')+1);
}
}else if (slot == 'B'){
address_buffer=50;
date_buffer= read_memory(address_buffer,30);
if (date_type == 'N'){
return_date= date_buffer.substring(1,date_buffer.indexOf('/')-1);
}else if (date_type == 'T'){
return_date= date_buffer.substring(date_buffer.indexOf('/')+1);
}
}else if (slot == 'C'){
address_buffer=100;
date_buffer= read_memory(address_buffer,30);
if (date_type == 'N'){
return_date= date_buffer.substring(1,date_buffer.indexOf('/')-1);
}else if (date_type == 'T'){
return_date= date_buffer.substring(date_buffer.indexOf('/')+1);
}
}
return return_date;
}
void setup()
{
pinMode(LED_BUILTIN, OUTPUT);
pinMode(PA10,INPUT);
pinMode( PA9,OUTPUT);
pinMode(D5,OUTPUT); //Set Pin of Buzzer
m1.attach(D6);
Serial.begin(9600);
digitalWrite(LED_BUILTIN, HIGH);
Serial1.begin(9600);
rtc.begin();
if (rtc.lostPower()){
rtc.adjust(DateTime(F(__DATE__),F(__TIME__)));
}
//Do animation with Servo and Slot 0=NO SLOT 90=SLOTA 180=SLOTB 270=SLOTC
m1.write(0);
delay(1000);
m1.write(90);
delay(1000);
m1.write(180);
delay(1000);
m1.write(270);
delay(1000);
sound();
edit_mode= false;
}
void loop(){
one_time=true;
while (Serial1.available()){
char date2= Serial1.read();
inputString_set+=date2;
Serial.print(date2);
edit_mode = true;
if (date2== targetString_A[stringIndex]){ //if the App wants the date of slot A
//Go to SLOTA
inputString[stringIndex]= date2;
stringIndex++;
if(stringIndex== sizeof(targetString_A)-1){
inputString[stringIndex]= '\0';
Serial.println(read_memory(0,30)); //Extract Dates from EEMPROM SLOTA ADDRESS 0
Serial1.println(read_memory(0,30));
m1.write(90);
delay(1000);
stringIndex= 0;
}
}else if(date2== targetString_B[stringIndex]){ //if the App wants the date of slot B
//Go to SLOTB
inputString[stringIndex]= date2;
stringIndex++;
if(stringIndex== sizeof(targetString_B)-1){
inputString[stringIndex]= '\0';
Serial.println(read_memory(50,30)); //Extract Dates from EEMPROM SLOTA ADDRESS 50
Serial1.println(read_memory(50,30));
m1.write(180);
delay(1000);
stringIndex= 0;
}
}else if (date2== targetString_C[stringIndex]){ //if the App wants the date of slot C
//Go to SLOTA
inputString[stringIndex]= date2;
stringIndex++;
if(stringIndex== sizeof(targetString_C)-1){
inputString[stringIndex]= '\0';
Serial.println(read_memory(100,30)); //Extract Dates from EEMPROM SLOTA ADDRESS 100
Serial1.println(read_memory(100,30));
m1.write(270);
delay(1000);
stringIndex= 0;
}
}else{
stringIndex= 0;
}
}
int s_a= inputString_set.indexOf(a_search); //If there is the S/A/ so the device wants to set the date of slot A
while(s_a !=-1 and one_time == true){
m1.write(90); //Go to SLOTA
delay(800);
int second= inputString_set.indexOf('/',5);
name_ext= inputString_set.substring(4,second);
int third= inputString_set.indexOf('/',second+1);
time_ext = inputString_set.substring(second+1,third);
Serial.println(name_ext);
Serial.println(time_ext);
String save_date_string = name_ext+"/"+time_ext;
save_memory(save_date_string,0);
inputString_set = "";
name_ext="";
time_ext = "";
one_time = false;
}
int s_b= inputString_set.indexOf(b_search); //If there is the S/B/ so the device wants to set the date of slot B
while(s_b !=-1 and one_time == true){
m1.write(180); //Go to SLOTB
delay(800);
int second= inputString_set.indexOf('/',5);
name_ext= inputString_set.substring(4,second);
int third= inputString_set.indexOf('/',second+1);
time_ext = inputString_set.substring(second+1,third);
Serial.println(name_ext);
Serial.println(time_ext);
String save_date_string = name_ext+"/"+time_ext;
save_memory(save_date_string,50);
inputString_set = "";
name_ext="";
time_ext = "";
one_time = false;
}
int s_c= inputString_set.indexOf(c_search); //If there is the S/C/ so the device wants to set the date of slot C
while(s_c !=-1 and one_time == true){
m1.write(270); //Go to SLOTC
delay(800);
int second= inputString_set.indexOf('/',5);
name_ext= inputString_set.substring(4,second);
int third= inputString_set.indexOf('/',second+1);
time_ext = inputString_set.substring(second+1,third);
Serial.println(name_ext);
Serial.println(time_ext);
String save_date_string = name_ext+"/"+time_ext;
save_memory(save_date_string,100);
inputString_set = "";
name_ext="";
time_ext = "";
one_time = false;
}
inputString_set = "";
name_ext="";
time_ext = "";
//delay(800);
//Start the time control and extract the data from the memory. After compare the date(time) with the RTC's time
DateTime now = rtc.now();
String now_time= String(now.hour())+ ":"+String(now.minute());
String a_time= get_date('A','T');
String b_time= get_date('B','T');
String c_time= get_date('C','T');
Serial.println(now_time);
if (now_time.equals(a_time)){
//SERVO GOES A
m1.write(90);
sound();
}else if (now_time.equals(b_time)){
//SERVO GOES B
m1.write(180);
sound();
}else if (now_time.equals(c_time)){
//SERVO GOES C
m1.write(270);
sound();
}else if (edit_mode == false){
m1.write(0);
}
delay(800);
edit_mode= false;
}
The code consists of several elements.
Void Setup consists of the declaration of the pins relating to communication with the HC-05, the Servo and the Buzzer. Then you set the serial communication to 9600. Within the function you also start the RTC module and set the time and date in case of loss of power. Finally the function ends with a movement between the slots and the sound playing via the soud() function.
void setup()
{
pinMode(LED_BUILTIN, OUTPUT);
pinMode(PA10,INPUT);
pinMode( PA9,OUTPUT);
pinMode(D5,OUTPUT); //Set Pin of Buzzer
m1.attach(D6);
Serial.begin(9600);
digitalWrite(LED_BUILTIN, HIGH);
Serial1.begin(9600);
rtc.begin();
if (rtc.lostPower()){
rtc.adjust(DateTime(F(__DATE__),F(__TIME__)));
}
//Do animation with Servo and Slot 0=NO SLOT 90=SLOTA 180=SLOTB 270=SLOTC
m1.write(0);
delay(1000);
m1.write(90);
delay(1000);
m1.write(180);
delay(1000);
m1.write(270);
delay(1000);
sound();
edit_mode= false;
}
Save Memory and GetDate are responsible for saving data from the slots and reading them respectively. The saving takes place on 3 different addresses. 0 for Slot A, 50 for SlotB and 100 for SlotC. Having provided a string with the relative Slot, the function divides the string into characters and saves it on the respective addresses.
void save_memory(String data,int address){
for (int j=0; j<data.length();j++){
EEPROM.write(address+j,data[j]);
}
EEPROM.write(address+data.length(),'\0');
}
String read_memory(int address,int lenght_str){
char dest[lenght_str+1];
for (int j=0; j<lenght_str;j++){
dest[j]=EEPROM.read(address+j);
if (dest[j] =='\0'){
break;
}
}
dest[lenght_str]= '\0';
return String(dest);
}
String get_date(char slot, char date_type){ //date_type or T=time or N=name
int address_buffer;
String date_buffer= "";
String return_date= "";
if (slot == 'A'){
address_buffer= 0;
date_buffer= read_memory(address_buffer,30);
if (date_type == 'N'){
return_date= date_buffer.substring(1,date_buffer.indexOf('/')-1);
}else if (date_type == 'T'){
return_date= date_buffer.substring(date_buffer.indexOf('/')+1);
}
}else if (slot == 'B'){
address_buffer=50;
date_buffer= read_memory(address_buffer,30);
if (date_type == 'N'){
return_date= date_buffer.substring(1,date_buffer.indexOf('/')-1);
}else if (date_type == 'T'){
return_date= date_buffer.substring(date_buffer.indexOf('/')+1);
}
}else if (slot == 'C'){
address_buffer=100;
date_buffer= read_memory(address_buffer,30);
if (date_type == 'N'){
return_date= date_buffer.substring(1,date_buffer.indexOf('/')-1);
}else if (date_type == 'T'){
return_date= date_buffer.substring(date_buffer.indexOf('/')+1);
}
}
return return_date;
}
Void Loop is the main function where the analysis of messages received via the HC-05 takes place. The communication messages are of your types.
The Get Date which consists of a message of the type (get/date/Slot) where Slot is a letter between "A-B-C" depending on the Slot requested. In this case the device responds with a message of the type (name_medicinale/time) containing the data relating to the requested Slot.
The second type concerns Set Dates. These messages are of the type (S/Slot/medicinal_name/time) where the S indicates "Set Mode" and Slot is a letter depending on the Slot. Name_medicine and time represent the name of the medicine and the time at which it must be taken. In both cases the system automatically aligns itself along the required Slot allowing the medicine to be inserted.
When it is not receiving data from the HC-05, the system obtains data relating to the three Slots approximately every 800ms and compares it with the time in real time. If one of these matches, the two devices align on the Slot and play the alarm sound for one minute.
void loop(){
one_time=true;
while (Serial1.available()){
char date2= Serial1.read();
inputString_set+=date2;
Serial.print(date2);
edit_mode = true;
if (date2== targetString_A[stringIndex]){ //if the App wants the date of slot A
//Go to SLOTA
inputString[stringIndex]= date2;
stringIndex++;
if(stringIndex== sizeof(targetString_A)-1){
inputString[stringIndex]= '\0';
Serial.println(read_memory(0,30)); //Extract Dates from EEMPROM SLOTA ADDRESS 0
Serial1.println(read_memory(0,30));
m1.write(90);
delay(1000);
stringIndex= 0;
}
}else if(date2== targetString_B[stringIndex]){ //if the App wants the date of slot B
//Go to SLOTB
inputString[stringIndex]= date2;
stringIndex++;
if(stringIndex== sizeof(targetString_B)-1){
inputString[stringIndex]= '\0';
Serial.println(read_memory(50,30)); //Extract Dates from EEMPROM SLOTA ADDRESS 50
Serial1.println(read_memory(50,30));
m1.write(180);
delay(1000);
stringIndex= 0;
}
}else if (date2== targetString_C[stringIndex]){ //if the App wants the date of slot C
//Go to SLOTA
inputString[stringIndex]= date2;
stringIndex++;
if(stringIndex== sizeof(targetString_C)-1){
inputString[stringIndex]= '\0';
Serial.println(read_memory(100,30)); //Extract Dates from EEMPROM SLOTA ADDRESS 100
Serial1.println(read_memory(100,30));
m1.write(270);
delay(1000);
stringIndex= 0;
}
}else{
stringIndex= 0;
}
}
int s_a= inputString_set.indexOf(a_search); //If there is the S/A/ so the device wants to set the date of slot A
while(s_a !=-1 and one_time == true){
m1.write(90); //Go to SLOTA
delay(800);
int second= inputString_set.indexOf('/',5);
name_ext= inputString_set.substring(4,second);
int third= inputString_set.indexOf('/',second+1);
time_ext = inputString_set.substring(second+1,third);
Serial.println(name_ext);
Serial.println(time_ext);
String save_date_string = name_ext+"/"+time_ext;
save_memory(save_date_string,0);
inputString_set = "";
name_ext="";
time_ext = "";
one_time = false;
}
int s_b= inputString_set.indexOf(b_search); //If there is the S/B/ so the device wants to set the date of slot B
while(s_b !=-1 and one_time == true){
m1.write(180); //Go to SLOTB
delay(800);
int second= inputString_set.indexOf('/',5);
name_ext= inputString_set.substring(4,second);
int third= inputString_set.indexOf('/',second+1);
time_ext = inputString_set.substring(second+1,third);
Serial.println(name_ext);
Serial.println(time_ext);
String save_date_string = name_ext+"/"+time_ext;
save_memory(save_date_string,50);
inputString_set = "";
name_ext="";
time_ext = "";
one_time = false;
}
int s_c= inputString_set.indexOf(c_search); //If there is the S/C/ so the device wants to set the date of slot C
while(s_c !=-1 and one_time == true){
m1.write(270); //Go to SLOTC
delay(800);
int second= inputString_set.indexOf('/',5);
name_ext= inputString_set.substring(4,second);
int third= inputString_set.indexOf('/',second+1);
time_ext = inputString_set.substring(second+1,third);
Serial.println(name_ext);
Serial.println(time_ext);
String save_date_string = name_ext+"/"+time_ext;
save_memory(save_date_string,100);
inputString_set = "";
name_ext="";
time_ext = "";
one_time = false;
}
inputString_set = "";
name_ext="";
time_ext = "";
//delay(800);
//Start the time control and extract the data from the memory. After compare the date(time) with the RTC's time
DateTime now = rtc.now();
String now_time= String(now.hour())+ ":"+String(now.minute());
String a_time= get_date('A','T');
String b_time= get_date('B','T');
String c_time= get_date('C','T');
Serial.println(now_time);
if (now_time.equals(a_time)){
//SERVO GOES A
m1.write(90);
sound();
}else if (now_time.equals(b_time)){
//SERVO GOES B
m1.write(180);
sound();
}else if (now_time.equals(c_time)){
//SERVO GOES C
m1.write(270);
sound();
}else if (edit_mode == false){
m1.write(0);
}
delay(800);
edit_mode= false;
}
Ther APP
The APP is made up of three buttons on the left which indicate the 3 Slots A-B-C. On the right, a Text-Box together with a Time Selector allows you to choose the name of the medicine and the time for each individual slot.
Finally, the Save button sends a message to the device with the updated data. At the bottom there is a button to connect to the device via Bluetooth.
Once the Slot has been connected and selected, every 1000ms a timer requests the slot data from the device and shows it in the Terxt-Box and in the Time Selector.
Among the project attachments I also included the.aia file to modify my APP on Mit App Inventor 2.
The Future StepsIn subsequent versions of the Pill Box I want to replace the Bluetooth connectivity with Wi-Fi connectivity thanks to the Blues Notecard Modules, allowing you to control and update the device even from afar thanks to the help of a Firebase Real-Time Database.
Thanks to Wi-Fi connectivity it will be possible to control the device using a voice assistant such as Alexa or Google via a customizable skill.
Comments