Things used in this project

Hardware components:
102110017%206
MediaTek Labs LinkIt Smart 7688 Duo
×1
SG90 servo
×2
2.4 inch USART HMI serial LCD display
×1
JQ8400 MP3 voice module
×1
3W 1.5 inches speaker
×1
Pen holder
2nd item
×1
0.4 mm thick*3mm torsion spring
16th item (50 per pack)
×8
M1*6mm screw
4th item, it is very easily broke, you should order more than 18
×18
m3*5mm screw
13th item (10 per pack)
×2
m3*8mm screw
14th item (10 per pack)
×4
Evian 500ml bottle
For pill container, you should buy one at the store near you :P
×1
2*10mm axle
1st row 1st item (20 per pack)
×8
Software apps and online services:
Ide web
Arduino IDE
USART HMI
for display module deployment
Hand tools and fabrication machines:
3drag
3D Printer (generic)

Custom parts and enclosures

Pill container cover
A simple decoration for the pill container
Pill container wheel frame
Ejector 7mm
Pill ejector for 7mm x 7mm x 12mm pill
Ejector 8.5mm
Pill ejector for 8.5mm x 8.5mm x 5mm pill
Ejector 10mm
Pill ejector for 10mm x 10mm x 5mm pill
Plug on the spinner servo, for plug on the pill container
Pill ejector gear on the ejector servo
Internal base
Core internal base frame
Internal base support
Support between PCB and internal base part
PCB support
Support between the pen holder and PCB
Pill slide, guide the pill slide out after eject
LCD support
Support between LCD frame and slide
LCD frame
Frame for the LCD module

Schematics

USART HMI design file
pillmachine_6aNXpRr7Bl.HMI
Pill Slot Cutout
Print it with a transparency and cut it out.
Pill Machine main circuit board
Test it with bread board first before soldering work.
pillmachine_ZrMj4sDtiW.fzz

Code

PillMachino.inoArduino
IoT Pill Machine core program
#include <Servo.h>
#include <SoftwareSerial.h>
#include <Process.h>

#define VOICE_MODULE
#define DEMO_ONLY
//#define VIDEO_TAKING_ONLY

#define SPINNER_PIN 5
#define EJECTOR_PIN 6

#define DISPLAY_RX 8
#define DISPLAY_TX 9

#ifdef VOICE_MODULE
#define VOICE_RX 10
#define VOICE_TX 11
#endif

#define EJECT_ANGLE 35
#define REST_ANGLE 180

#define USER_INFO_URL "http://YOUR_IP:YOUR_PORT/user_info"
#define NEXT_SCHEDULE_URL "http://YOUR_IP:YOUR_PORT/next_schedule"
#define TAKEN_URL "http://YOUR_IP:YOUR_PORT/taken"

Servo spinner_servo;
Servo ejector_servo;
SoftwareSerial display_serial(DISPLAY_RX, DISPLAY_TX);
#ifdef VOICE_MODULE
SoftwareSerial voice_serial(VOICE_RX, VOICE_TX);
#endif

int pill_slot_angle[] = {15, 60, 105, 150};

byte buf_page_cmd[] = {'p', 'a', 'g', 'e', ' ', '0', 0xFF, 0xFF, 0xFF};
byte buf_set_str_cmd[] = {'s', 'c', 'h', 'e', '_', 's', 't', 'r', '.', 't', 'x', 't', '=', '"', '0', '0', ':', '0', '0', '"', 0xFF, 0xFF, 0xFF};

#ifdef VOICE_MODULE
// voice volume 1-30
//byte buf_voice_vol[] = {0xAA, 0x13, 0x01, 0x0F, 0xCD}; // command to set volume 15
//byte buf_voice_vol[] = {0xAA, 0x13, 0x01, 0x14, 0xD2}; // command to set volume 20
byte buf_voice_vol[] = {0xAA, 0x13, 0x01, 0x19, 0xD7}; // command to set volume 25
//byte buf_voice_vol[] = {0xAA, 0x13, 0x01, 0x1E, 0xDC}; // command to set volume 30

byte buf_play[4][6] = {
  {0xAA, 0x07, 0x02, 0x00, 0x01, 0xB4}, // command to play 01.mp3
  {0xAA, 0x07, 0x02, 0x00, 0x02, 0xB5}, // command to play 02.mp3
  {0xAA, 0x07, 0x02, 0x00, 0x03, 0xB6}, // command to play 03.mp3
  {0xAA, 0x07, 0x02, 0x00, 0x04, 0xB7}  // command to play 04.mp3
};
#endif

// function declaration
void ui_page(byte page);
void wait_next_schedule();
void eject_pill(byte pill_slot);
void get_user_info();
void get_next_schedule();
void push_taken();

int cur_step = 0;
int delay_time = 0;

void setup() {
  // Initialize Serial For Debug Output
  Serial.begin(9600);

  // Initialize Pill Ejection Module
  spinner_servo.attach(SPINNER_PIN);
  spinner_servo.write(0);
  ejector_servo.attach(EJECTOR_PIN);
  ejector_servo.write(REST_ANGLE);

  // Initialize Display Module
  display_serial.begin(9600);

#ifdef VOICE_MODULE
  // Initialize Voice Module
  voice_serial.begin(9600);
  voice_serial.write(buf_voice_vol, 5);
#endif

  // display loading screen
  ui_page(0);

  // Initialize Bridge
  Bridge.begin();

  // display splash screen
  ui_page(1);
}

// the loop routine runs over and over again forever:
void loop() {
  display_serial.listen();
  while (display_serial.available() > 0) {
    char in_char = display_serial.read();
    Serial.write(in_char);
    switch (in_char) { // check signal from which screen
      case '1':
        // display user info screen
        ui_page(2);
        break;
      case '2':
        // display next schedule screen
        ui_page(3);
        break;
#ifdef VIDEO_TAKING_ONLY
      case '3': // should be triggered by timer
        wait_next_schedule();
        break;
#endif
      case '4':
        if (cur_step == 0) {
          eject_pill(0);
          eject_pill(1);
          eject_pill(2);
          ui_page(5);
        } else if (cur_step == 1) {
          eject_pill(0);
          eject_pill(2);
          ui_page(6);
        } else {
          eject_pill(1);
          ui_page(7);
        }
        spinner_servo.write(0);
        break;
      case '5':
        push_taken();
        cur_step = 1;
        ui_page(3);
        break;
      case '6':
        push_taken();
        cur_step = 2;
        ui_page(3);
        break;
      case '7':
        push_taken();
        cur_step = 0;
        ui_page(3);
        break;
      default:
        break;
    }
    while (display_serial.available() > 0) {
      char c = display_serial.read();
    }
  }
}

void ui_page(byte page) {
  switch (page) {
    case 0:
      buf_page_cmd[5] = '0';
      display_serial.write(buf_page_cmd, sizeof(buf_page_cmd));
      break;
    case 1:
      buf_page_cmd[5] = '1';
      display_serial.write(buf_page_cmd, sizeof(buf_page_cmd));
      break;
    case 2:
      buf_page_cmd[5] = '2';
      display_serial.write(buf_page_cmd, sizeof(buf_page_cmd));
      //TODO: get and display user info from cloud server
      //get_user_info();
#ifdef VOICE_MODULE
      voice_serial.write(buf_play[0], 6);
#endif
      break;
    case 3:
      buf_page_cmd[5] = '3';
      display_serial.write(buf_page_cmd, sizeof(buf_page_cmd));
      buf_set_str_cmd[0] = 's';
      buf_set_str_cmd[1] = 'c';
      buf_set_str_cmd[2] = 'h';
#ifdef DEMO_ONLY
      if (cur_step == 0) {
        buf_set_str_cmd[14] = '1';
        buf_set_str_cmd[15] = '4';
        display_serial.write(buf_set_str_cmd, sizeof(buf_set_str_cmd));
      } else if (cur_step == 1) {
        buf_set_str_cmd[14] = '2';
        buf_set_str_cmd[15] = '0';
        display_serial.write(buf_set_str_cmd, sizeof(buf_set_str_cmd));
      } else {
        buf_set_str_cmd[14] = '0';
        buf_set_str_cmd[15] = '8';
        display_serial.write(buf_set_str_cmd, sizeof(buf_set_str_cmd));
      }
#else
      //TODO: get and display time from cloud schedule
      //get_next_schedule();
      //buf_set_str_cmd[14] =
      //buf_set_str_cmd[15] =
      //buf_set_str_cmd[17] =
      //buf_set_str_cmd[18] =
      //display_serial.write(buf_set_str_cmd, sizeof(buf_set_str_cmd));
#endif
#ifdef VOICE_MODULE
      voice_serial.write(buf_play[1], 6);
#endif
#ifdef VIDEO_TAKING_ONLY
      // do nothing
#else
      wait_next_schedule();
#endif
      break;
    case 4:
      buf_page_cmd[5] = '4';
      display_serial.write(buf_page_cmd, sizeof(buf_page_cmd));
      buf_set_str_cmd[0] = 't';
      buf_set_str_cmd[1] = 'i';
      buf_set_str_cmd[2] = 'm';
#ifdef DEMO_ONLY
      if (cur_step == 0) {
        buf_set_str_cmd[14] = '1';
        buf_set_str_cmd[15] = '4';
        display_serial.write(buf_set_str_cmd, sizeof(buf_set_str_cmd));
      } else if (cur_step == 1) {
        buf_set_str_cmd[14] = '2';
        buf_set_str_cmd[15] = '0';
        display_serial.write(buf_set_str_cmd, sizeof(buf_set_str_cmd));
      } else {
        buf_set_str_cmd[14] = '0';
        buf_set_str_cmd[15] = '8';
        display_serial.write(buf_set_str_cmd, sizeof(buf_set_str_cmd));
      }
#else
      //TODO: display time from cloud schedule
      //buf_set_str_cmd[14] =
      //buf_set_str_cmd[15] =
      //buf_set_str_cmd[17] =
      //buf_set_str_cmd[18] =
      //display_serial.write(buf_set_str_cmd, sizeof(buf_set_str_cmd));
#endif
#ifdef VOICE_MODULE
      voice_serial.write(buf_play[2], 6);
#endif
      break;
    case 5:
      buf_page_cmd[5] = '5';
      display_serial.write(buf_page_cmd, sizeof(buf_page_cmd));
#ifdef VOICE_MODULE
      voice_serial.write(buf_play[3], 6);
#endif
      break;
    case 6:
      buf_page_cmd[5] = '6';
      display_serial.write(buf_page_cmd, sizeof(buf_page_cmd));
#ifdef VOICE_MODULE
      voice_serial.write(buf_play[3], 6);
#endif
      break;
    case 7:
      buf_page_cmd[5] = '7';
      display_serial.write(buf_page_cmd, sizeof(buf_page_cmd));
#ifdef VOICE_MODULE
      voice_serial.write(buf_play[3], 6);
#endif
      break;
    default:
      break;
  }
}

void wait_next_schedule() {
  //TODO: use timer
#ifdef DEMO_ONLY
  Serial.println("wait 10 seconds");
  delay(10000);
#else
  Serial.print("wait ");
  Serial.print(delay_time);
  Serial.println(" milliseconds");
  delay(delay_time);
#endif
  ui_page(4);
}

void eject_pill(byte pill_slot) {
  spinner_servo.write(pill_slot_angle[pill_slot]);
  delay(1000);
  ejector_servo.write(EJECT_ANGLE);
  delay(1000);
  ejector_servo.write(REST_ANGLE);
  delay(1000);
}

void get_user_info() {
  // Launch "curl" command and get Arduino ascii art logo from the network
  // curl is command line program for transferring data using different internet protocols
  Process p;        // Create a process and call it "p"
  p.begin("curl");  // Process that launch the "curl" command
  // Add the URL parameter to "curl"
  p.addParameter("--data");
  p.addParameter("id=pill_machine");
  p.addParameter(USER_INFO_URL);
  p.run();      // Run the process and wait for its termination

  // A process output can be read with the stream methods
  while (p.available()>0) {
    char c = p.read();
    Serial.print(c);
    //TODO: save user info to variable
  }
  // Ensure the last bit of data is sent.
  Serial.flush();
}

void get_next_schedule() {
  // Launch "curl" command and get Arduino ascii art logo from the network
  // curl is command line program for transferring data using different internet protocols
  Process p;        // Create a process and call it "p"
  p.begin("curl");  // Process that launch the "curl" command
  // Add the URL parameter to "curl"
  p.addParameter("--data");
  p.addParameter("id=pill_machine");
  p.addParameter(NEXT_SCHEDULE_URL);
  p.run();      // Run the process and wait for its termination

  // A process output can be read with the stream methods
  while (p.available()>0) {
    char c = p.read();
    Serial.print(c);
    //TODO: save next schedule to variable
    //delay_time =
  }
  // Ensure the last bit of data is sent.
  Serial.flush();
}

void push_taken() {
  // Launch "curl" command and get Arduino ascii art logo from the network
  // curl is command line program for transferring data using different internet protocols
  Process p;        // Create a process and call it "p"
  p.begin("curl");  // Process that launch the "curl" command
  // Add the URL parameter to "curl"
  p.addParameter("--data");
  p.addParameter("id=pill_machine");
  p.addParameter(TAKEN_URL);
  p.run();      // Run the process and wait for its termination

  // A process output can be read with the stream methods
  while (p.available()>0) {
    char c = p.read();
    Serial.print(c);
  }
  // Ensure the last bit of data is sent.
  Serial.flush();
}

Project logs

Pill container and ejector status update
Project log #2
On Dec 2, 2016 at 2:28 PM - 0 comments

The pill container can store 8 types of pill. Use 1 servo turn to the target pill slot and use another servo eject 1 pill each time.

https://youtu.be/Jiq4_0o5Wq8

Read previous logs

Credits

Replications

Did you replicate this project? Share it!

I made one

Love this project? Think it could be improved? Tell us what you think!

Give feedback

Comments

Similar projects you might like

Driving Parameters Analysis System
Advanced
  • 1,547
  • 21

Full instructions

Smart system for analyzing the driving skills of a person using IOT and Cloud Computing powered by Mediatek LinkIt One

Employee safety controller
Advanced
  • 712
  • 10

Work in progress

Safety controller care about employee health

Employee safety controller

Team KNITU-Embedded Lab

Air Quality System using LinkIt ONE and MCS
Advanced
  • 2,363
  • 24

Monitor the Temperature, Humidity, Pressure, Air Quality, Dust particle concentration online & even detect Fire using LinkIt ONE & Sensors.

Energy Planner
Advanced
  • 281
  • 6

Full instructions

Solar panels produce when there is sun, and during daytime I can earn money with my generated energy. When should I use the washing machine?

Smart Furniture Using Linkit Mediatek 7688 Duo
Advanced
  • 575
  • 9

Protip

We are making smart furniture and our first prototype is for smart computer table.

Barbecue Planner with Amazon DRS integration
Advanced
  • 257
  • 5

Full instructions

A barbecue shaped alarm that tells you when it’s going to be barbecue weather. It will order your supplies through the Amazon DRS system.

Add projectSign up / Login