Close
0%
0%

MEDIC Mini

Medic Mini is a handheld ESP32-C6 device that checks symptoms using simple button inputs.

Similar projects worth following
Greetings everyone, and welcome back! Meet Medic Mini, a handheld self-diagnostic tool designed to guide users through basic symptom checks with just a few button presses. Whether you're feeling under the weather or just want a quick health triage, Medic Mini offers a fast, intuitive way to assess common symptoms without needing an app or internet.

At its core, Medic Mini runs on an ESP32-C6 paired with a 1.47" Waveshare display, housed inside a custom PCB and a 3D-printed enclosure I designed from scratch. The interface is clean and purposeful: users are prompted with one symptom at a time and respond using three tactile buttons—Yes, No, or Not Sure.

I designed Medic Mini to serve as a simple, standalone tool for diagnosing basic medical conditions like fever, viral infections, and fatigue. The current logic is based on common symptoms and straightforward decision rules, but this is just the beginning. I plan to expand its diagnostic database by collecting more symptom data and working with medical professionals to refine the logic.

Based on the pattern of responses, the device offers a basic diagnostic suggestion, making health checks feel as simple as flipping a switch.

This Article covers the whole build process of this project, so let's get started with the build.

Self-Diagnostic Idea

The concept behind Medic Mini was born out of a simple frustration: most health checkers are either too complex, too expensive, or too slow.

I wanted something instant, intuitive, and portable, a device that could walk someone through basic symptoms without needing an app or the internet.

The idea came from observing how people respond to simple yes/no questions when describing how they feel. That’s where the logic started:

• Ask one symptom at a time

• Let the user respond with a button press

• Track the pattern of responses

• Offer a basic result or suggestion

It’s not meant to replace a doctor; it’s meant to give clarity when you’re unsure. Whether it’s for kids, travelers, or just quick reassurance, the goal was to make a health checkup feel like flipping a switch.

The spark really came after watching my own parents. One day, my mom had a headache that seemed minor but by the next day, it turned out to be viral. That made me realize how many symptoms are actually straightforward to track and interpret.

So I thought, why not build a diagnostic device that can guide people through that process?

Right now, the data I’ve collected is limited, but the potential is huge. With input from doctors and medical professionals, we could expand the database, refine the logic, and even integrate sensors to read pulse, temperature, and other vitals.

Medic Mini is just the start; the goal is to make accessible, intelligent health tools that anyone can use.

Basic Setup- ESP32 C6 Devkit Breadboard Edition

To get started with the electronics, I built a simple breadboard setup using the ESP32-C6 DevKit connected to a 320×240 ILI9341 display.

For user interaction, I added three tactile buttons—Yes, No, and Not Sure—mapped to GPIO pins. This setup served as our base model for testing the core logic and UI flow.

Once everything is working reliably, the plan is to shrink the entire system into a compact, handheld device using a custom PCB and a 3D-printed enclosure.

We started by placing the ESP32-C6 DevKit on a breadboard alongside a 320×240 ILI9341 display and three push buttons for user input.

Using the provided wiring diagram, we connected the display to the ESP32 Devkit in the following order.

  • DISPLAY's MOSI to GPIO6
  • SCK to GPIO7
  • Chip Select to GPIO10
  • Reset to GPIO11
  • DC to GPIO12
  • The LED Pin of Display goes to 3V3 of the DevKit.
  • VCC goes to 5V
  • GND to GND

This setup gave us a clean, testable base to test the UI and logic before moving on to a custom PCB and enclosure.

CODE for Breadboard Edition

For our Breadboard setup, we use the following code and let's have a quick breakdown of how it works.

#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>
#include <SPI.h>
// Display pins
#define TFT_CS 10
#define TFT_DC 12
#define TFT_RST 11
// Button pins
#define BTN_YES 15
#define BTN_NO 23
#define BTN_UNSURE 22
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_RST);
// Symptom list
const char* symptoms[] = {
"Headache", "Fever", "Cough", "Fatigue"...
Read more »

  • 1
    PCB ASSEMBLY PROCESS
    • We start the PCB assembly process by applying solder paste to each SMD component pad using a solder paste syringe. We’re using 63-37 SnPb paste, which has a melting temperature of 200°C.
    • Next, we begin the pick-and-place process, which involves picking up SMD components using ESD tweezers and placing them in their designated locations.
    • Once placed, we lift the circuit and set it on our SMD reflow hotplate. The hotplate heats the PCB from below until it reaches the solder paste’s melting temperature. As soon as the PCB hits 200°C, the solder paste melts and secures the components to the board.
    • After that, we place a 6×6 tactile switch on the bottom side of the PCB and solder it in place using a soldering iron.
    • The rest of the through-hole components are added from the top side, including three 4×4 tactile switches and one Type-C port.
    • Finally, we place the Waveshare ESP32 board in its position and solder it using a soldering iron. We simply align it over the mounting pads and use excess solder on each pad to join the ESP32 board to the PCB.
  • 2
    POWER SOURCE

    For the power source of this project, we are utilizing a 3.7V 600mAh Li-Po cell, which can provide a total runtime of more than 10 hours, which is pretty decent, considering we won’t be using it continuously. After diagnosing, we can turn the device OFF, which gives us a backup of 2–3 days depending on usage.

    We solder the positive and negative terminals of the lithium cell to the battery connectors provided on the circuit.

    By pressing the 6×6 tactile switch added on the backside of the circuit, the device turns ON. Double-tapping the switch turns the device OFF.

    We can also charge this setup by plugging in a 5V supply via the provided Type-C port. During charging, the indicator LED will blink, and it turns stable once the lithium cell is fully charged.

  • 3
    CODE for Waveshare ESP32 C6 1.47 Display

    This is the finalized code running on the Medic Mini device. It differs slightly from our earlier demo sketch due to changes in the display and I/O configuration. The demo version used an ILI9341 display with the ESP32-C6 DevKit, which required a different set of GPIO assignments and library setup.

    In contrast, the final build uses the Waveshare ESP32-C6 1.47" display module.

    The below Breakdown highlights all the key differences introduced in this updated sketch.

    #include 
    
    // LCD pin map for ESP32-C6
    #define LCD_MOSI 6
    #define LCD_SCLK 7
    #define LCD_CS   14
    #define LCD_DC   15
    #define LCD_RST  21
    #define LCD_BL   22
    // Button pins
    #define BTN_YES     9
    #define BTN_NO      18
    #define BTN_UNSURE  19
    const char* symptoms[] = {
    "Headache",
    "Fever",
    "Cough",
    "Fatigue",
    "Nausea"
    };
    const int symptomCount = sizeof(symptoms) / sizeof(symptoms[0]);
    int responses[symptomCount]; // -1 = not answered, 0 = No, 1 = Yes, 2 = Not Sure
    int currentSymptom = -1;
    bool symptomDrawn = false;
    enum ScreenState {
    SCREEN_STARTUP,
    SCREEN_SYMPTOM,
    SCREEN_RESULT
    };
    ScreenState currentScreen = SCREEN_STARTUP;
    ScreenState lastScreen = SCREEN_STARTUP;
    // Button states
    bool lastYes = false;
    bool lastNo = false;
    bool lastUnsure = false;
    unsigned long lastInputTime = 0;
    const unsigned long inputLockout = 300;
    Arduino_DataBus *bus = new Arduino_ESP32SPI(
    LCD_DC, LCD_CS, LCD_SCLK, LCD_MOSI, GFX_NOT_DEFINED
    );
    Arduino_GFX *gfx = new Arduino_ST7789(
    bus,
    LCD_RST,
    2,
    true,
    172,
    320,
    34, 0,
    34, 0
    );
    // Stable press detection
    bool isStablePress(int pin, bool& lastState) {
    bool current = digitalRead(pin) == LOW;
    bool pressed = current && !lastState;
    lastState = current;
    return pressed;
    }
    void drawStartup() {
    gfx->fillScreen(BLACK);
    // Title split: Medic / Mini
    gfx->setTextSize(3);
    gfx->setTextColor(CYAN);
    gfx->setCursor(20, 40);
    gfx->println("Medic");
    gfx->setCursor(20, 80);
    gfx->println("Mini");
    // Subtitle split: Symptom / Checker
    gfx->setTextSize(2);
    gfx->setTextColor(WHITE);
    gfx->setCursor(20, 130);
    gfx->println("Symptom");
    gfx->setCursor(20, 160);
    gfx->println("Checker");
    // Red medical plus sign
    gfx->fillRect(80, 220, 20, 60, RED);  // vertical bar
    gfx->fillRect(60, 240, 60, 20, RED);  // horizontal bar
    }
    void drawSymptomPrompt(const char* symptom, int response) {
    gfx->fillScreen(BLACK);
    gfx->setTextSize(2);
    gfx->setTextColor(WHITE);
    gfx->setCursor(10, 80);
    gfx->println("Do you have");
    gfx->setCursor(10, 110);
    gfx->print(symptom);
    gfx->println("?");
    // Centered response label
    gfx->setTextSize(3);
    int centerX = 86;
    if (response == 1) {
    gfx->setTextColor(GREEN);
    gfx->setCursor(centerX - 30, 180);
    gfx->println("Yes");
    } else if (response == 0) {
    gfx->setTextColor(RED);
    gfx->setCursor(centerX - 30, 180);
    gfx->println("No");
    } else if (response == 2) {
    gfx->setTextColor(YELLOW);
    gfx->setCursor(centerX - 70, 180);
    gfx->println("Not Sure");
    }
    }
    void drawResult() {
    gfx->fillScreen(BLACK);
    gfx->setCursor(10, 60);
    gfx->setTextSize(2);
    gfx->setTextColor(GREEN);
    gfx->println("Processing...");
    delay(1000);
    int yesCount = 0;
    for (int i = 0; i < symptomCount; i++) {
    if (responses[i] == 1) yesCount++;
    }
    gfx->fillScreen(BLACK);
    gfx->setTextSize(2);
    gfx->setTextColor(WHITE);
    if (yesCount >= 3) {
    gfx->setCursor(10, 100);
    gfx->println("Possible");
    gfx->setCursor(10, 170);
    gfx->println("match:");
    gfx->setCursor(10, 210);
    gfx->setTextColor(YELLOW);
    gfx->println("Flu or Viral");
    } else if (yesCount == 2) {
    gfx->setCursor(10, 100);
    gfx->println("Mild symptoms");
    gfx->setCursor(10, 140);
    gfx->println("Monitor &");
    gfx->setCursor(10, 170);
    gfx->println("rest");
    } else {
    gfx->setCursor(10, 100);
    gfx->println("No major");
    gfx->setCursor(10, 140);
    gfx->println("match found");
    }
    gfx->setCursor(10, 200);
    gfx->setTextSize(1);
    gfx->setTextColor(CYAN);
    gfx->println("Press YES to restart");
    }
    void setup() {
    pinMode(LCD_BL, OUTPUT);
    digitalWrite(LCD_BL, HIGH);
    pinMode(BTN_YES, INPUT_PULLUP);
    pinMode(BTN_NO, INPUT_PULLUP);
    pinMode(BTN_UNSURE, INPUT_PULLUP);
    for (int i = 0; i < symptomCount; i++) {
    responses[i] = -1;
    }
    gfx->begin();
    drawStartup();
    }
    void loop() {
    unsigned long now = millis();
    if (currentScreen != lastScreen) {
    if (currentScreen == SCREEN_STARTUP) {
    drawStartup();
    } else if (currentScreen == SCREEN_RESULT) {
    drawResult();
    }
    lastScreen = currentScreen;
    }
    if (currentScreen == SCREEN_SYMPTOM &&
    currentSymptom < symptomCount &&
    !symptomDrawn) {
    drawSymptomPrompt(symptoms[currentSymptom], responses[currentSymptom]);
    symptomDrawn = true;
    }
    if (currentScreen == SCREEN_STARTUP &&
    (isStablePress(BTN_YES, lastYes) || isStablePress(BTN_NO, lastNo) || isStablePress(BTN_UNSURE, lastUnsure))) {
    currentSymptom = 0;
    currentScreen = SCREEN_SYMPTOM;
    symptomDrawn = false;
    delay(300);
    }
    if (currentScreen == SCREEN_SYMPTOM && currentSymptom < symptomCount) {
    if (now - lastInputTime > inputLockout) {
    if (isStablePress(BTN_YES, lastYes)) {
    responses[currentSymptom] = 1;
    drawSymptomPrompt(symptoms[currentSymptom], 1);
    delay(500);
    currentSymptom++;
    symptomDrawn = false;
    lastInputTime = now;
    } else if (isStablePress(BTN_NO, lastNo)) {
    responses[currentSymptom] = 0;
    drawSymptomPrompt(symptoms[currentSymptom], 0);
    delay(500);
    currentSymptom++;
    symptomDrawn = false;
    lastInputTime = now;
    } else if (isStablePress(BTN_UNSURE, lastUnsure)) {
    responses[currentSymptom] = 2;
    drawSymptomPrompt(symptoms[currentSymptom], 2);
    delay(500);
    currentSymptom++;
    symptomDrawn = false;
    lastInputTime = now;
    }
    if (currentSymptom >= symptomCount) {
    currentScreen = SCREEN_RESULT;
    }
    }
    }
    if (currentScreen == SCREEN_RESULT && isStablePress(BTN_YES, lastYes)) {
    for (int i = 0; i < symptomCount; i++) {
    responses[i] = -1;
    }
    currentSymptom = 0;
    currentScreen = SCREEN_SYMPTOM;
    symptomDrawn = false;
    delay(300);
    }
    }

    In our main sketch, we use the Arduino GFX Library to drive the ST7789 display.

    The following configuration sets up the screen dimensions, rotation, and offset parameters tailored to the Waveshare ESP32-C6 1.47" module.

    Arduino_GFX *gfx = new Arduino_ST7789(bus,LCD_RST,2,          // rotationtrue,       // IPS172,        // width320,        // height34, 0,      // X offset34, 0       // Y offset);

    We configured the pin mapping to match the default wiring of the Waveshare ESP32-C6 1.47" display. These assignments were based on the official Waveshare Wiki documentation.

    #define LCD_MOSI 6#define LCD_SCLK 7#define LCD_CS   14#define LCD_DC   15#define LCD_RST  21#define LCD_BL   22

    Next comes the button input.

    #define BTN_YES     9#define BTN_NO      18#define BTN_UNSURE  19

    The rest of the code logic remains unchanged from the previous demo sketch. The only modifications made here are the updated display configuration and revised input button mappings to match the final hardware.

    We uploaded the code into our ESP32 board and moved onto the next step, which was the enclosure assembly. From next, we cannot upload code into ESP32 because its USB will be inaccessible.

View all 6 instructions

Enjoy this project?

Share

Discussions

Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates