Skip to content

Claude Chats to Update Functionality of my App

I used a lot of back and forth with claude to get what I want the way I want it.. so I will put it all in here so you can see our conversation.

add to my UI

Prompt:

Now I want to work more on changing the functionality. I would like to add the second notification and the abilty to change how often it notifies. I would like to have a first message to the Pill Taker that the pill is ready. Then the rest are reminders base on user set repeat window until 21 minutes have passed. The care taker is notified if (A) the pill is taken, (B) the pill is not taken after 15 minutes, (C) a second reminder if the pill is not taken after 20 minutes. I will ask claude to add this functionality along with the ability to set the reminder repeat window for the Pill Taker. I will also ask them to set how many reminders they have left before the caretaker is notified, based on the repeat time that they set.

Looking at your current code, here's what needs to change to support this:


New state variables needed (Section 2)

int  repeatWindowMinutes = 5;     // how often to re-notify pill taker
bool caretakerEnabled    = false; // toggle from UI
unsigned long lastReminderTime = 0; // track last reminder sent
int  reminderCount       = 0;     // how many reminders sent so far

// Caretaker Telegram ID (separate from pill taker)
const char* caretakerChatID = "XXXXXXXXX";  // Section 1

New notification flow (replaces checkPillWindow logic)

t=0 min  → dispense → message pill taker "Your pill is ready"
t=repeat → if not taken → remind pill taker "Reminder: pill waiting"
t=repeat × 2 → if not taken → remind again
...continues until 21 minutes...
t=15 min → if not taken → notify caretaker "Pill not yet taken (15 min)"
t=20 min → if not taken → notify caretaker again "Second reminder (20 min)"
t=21 min → end cycle regardless
if taken at any point → notify caretaker "Pill was taken at HH:MM"

Two sendTelegram targets needed

Right now sendTelegram() only sends to one chat ID. You need a second function:

void sendTelegramTo(String chatID, String message) {
  // same as sendTelegram() but takes chatID as parameter
}

void sendTelegram(String message) {
  sendTelegramTo(telegramChatID, message);  // pill taker
}

void notifyCaretaker(String message) {
  if (caretakerEnabled) sendTelegramTo(caretakerChatID, message);
}

New /window route needs more parameters

Currently it only accepts minutes. Needs to also accept repeat and caretaker:

/window?minutes=21&repeat=5&caretaker=1

And your setWindow() JS function needs to send all three values.


UI additions needed

Your repeatMinutes input and caretaker checkbox are already in the HTML — they just need to be wired into the setWindow() fetch call, and you need to add a caretaker chat ID field somewhere (or hardcode it in Section 1).


Want me to write out the updated checkPillWindow() function and the updated handleSetWindow() and JS first, since that's the core logic change?

yes, write the script we talked about

PROMPT: yes with complete information on how to integrate it

Here's everything you need. I'll go section by section so you know exactly what to replace or add.


SECTION 1 — Add caretaker chat ID

Find your Telegram block and add one line:

// --- Telegram ---
const char* telegramToken     = "8747327752:AAF6uDRPqW-2ONHUdVhR26Chlnansd879oQ";
const char* telegramChatID    = "8155911472";  // pill taker
const char* caretakerChatID   = "XXXXXXXXX";   // replace with caretaker's chat ID

SECTION 2 — Add new state variables

Find your existing state variables block and add these after checkWindowMinutes:

int  repeatWindowMinutes  = 5;      // how often to remind pill taker
bool caretakerEnabled     = false;  // set from web UI checkbox
bool caretaker15Sent      = false;  // 15-min caretaker alert fired
bool caretaker20Sent      = false;  // 20-min caretaker alert fired
bool caretakerTakenSent   = false;  // "pill was taken" notice fired
unsigned long lastReminderTime = 0; // millis() of last pill taker reminder

SECTION 3 — Update dispenseOnePill() and resetPillCycle()

Replace your existing dispenseOnePill() and resetPillCycle() with these:

void dispenseOnePill() {
  Serial.println("Dispensing pill...");
  moveWheel(STEPS_PER_DISPENSE);
  digitalWrite(LED_PIN, LOW);

  pillDispensed       = true;
  pillCheckActive     = true;
  alertSent           = false;
  caretaker15Sent     = false;
  caretaker20Sent     = false;
  caretakerTakenSent  = false;
  reminderCount       = 0;
  dispenseTime        = millis();
  lastReminderTime    = millis();

  sendTelegram("Your pill is ready to take!");
  Serial.println("Pill dispensed. Notification sent.");
}

void resetPillCycle() {
  pillDispensed       = false;
  pillCheckActive     = false;
  alertSent           = false;
  caretaker15Sent     = false;
  caretaker20Sent     = false;
  caretakerTakenSent  = false;
  reminderCount       = 0;
  lastReminderTime    = 0;
  digitalWrite(LED_PIN, HIGH);
  Serial.println("Pill cycle reset.");
}

SECTION 5 — Replace sendTelegram with two-target system

Replace your entire sendTelegram() function with these three functions:

void sendTelegramTo(String chatID, String message) {
  WiFiClientSecure client;
  client.setInsecure();

  Serial.println("Connecting to Telegram...");
  if (!client.connect("api.telegram.org", 443)) {
    Serial.println("Telegram connection failed!");
    return;
  }

  message.replace(" ", "%20");

  String url = "/bot" + String(telegramToken) +
               "/sendMessage?chat_id=" + chatID +
               "&text=" + message;

  client.print(String("GET ") + url + " HTTP/1.1\r\n" +
               "Host: api.telegram.org\r\n" +
               "Connection: close\r\n\r\n");

  unsigned long timeout = millis();
  while (client.connected() && millis() - timeout < 5000) {
    if (client.available()) {
      Serial.println(client.readStringUntil('\n'));
    }
  }
  client.stop();
  Serial.println("Telegram done.");
}

// Send to pill taker
void sendTelegram(String message) {
  sendTelegramTo(String(telegramChatID), message);
}

// Send to caretaker only if enabled
void notifyCaretaker(String message) {
  if (caretakerEnabled) {
    sendTelegramTo(String(caretakerChatID), message);
  }
}

SECTION 6 — Replace checkPillWindow() entirely

Replace your existing checkPillWindow() with this:

void checkPillWindow() {
  if (!pillCheckActive) return;

  unsigned long elapsed = millis() - dispenseTime;
  unsigned long elapsedMin = elapsed / 60000UL;

  // --- Check if pill was taken ---
  static unsigned long lastSensorCheck = 0;
  if (millis() - lastSensorCheck > 10000) {
    lastSensorCheck = millis();
    if (isPillTaken()) {
      Serial.println("Pill taken! Notifying caretaker.");

      // Build time string for caretaker message
      struct tm timeinfo;
      char timeStr[9] = "--:--";
      if (getLocalTime(&timeinfo)) {
        strftime(timeStr, sizeof(timeStr), "%H:%M", &timeinfo);
      }

      notifyCaretaker("Pill was taken at " + String(timeStr));
      resetPillCycle();
      return;
    }
  }

  // --- Pill taker reminders on repeat window ---
  unsigned long repeatMs = (unsigned long)repeatWindowMinutes * 60UL * 1000UL;
  if (!alertSent && millis() - lastReminderTime > repeatMs) {
    if (elapsedMin < 21) {
      reminderCount++;
      int remindersLeft = max(0, (int)((21 - elapsedMin) / repeatWindowMinutes));
      String msg = "Reminder: your pill is waiting! (" +
                   String(remindersLeft) + " reminder(s) left before caretaker notified)";
      sendTelegram(msg);
      lastReminderTime = millis();
      Serial.println("Reminder sent. Count: " + String(reminderCount));
    }
  }

  // --- Caretaker alert at 15 minutes ---
  if (!caretaker15Sent && elapsedMin >= 15) {
    notifyCaretaker("Pill reminder: pill has not been taken after 15 minutes.");
    caretaker15Sent = true;
    Serial.println("Caretaker 15-min alert sent.");
  }

  // --- Caretaker alert at 20 minutes ---
  if (!caretaker20Sent && elapsedMin >= 20) {
    notifyCaretaker("Second reminder: pill still not taken after 20 minutes.");
    caretaker20Sent = true;
    Serial.println("Caretaker 20-min alert sent.");
  }

  // --- End cycle at 21 minutes ---
  if (elapsedMin >= 21) {
    Serial.println("21 minute window expired. Ending cycle.");
    if (!isPillTaken()) {
      alertSent = true;
    }
    digitalWrite(LED_PIN, HIGH);
    pillCheckActive = false;
    pillDispensed   = false;
  }
}

SECTION 7 — Update handleSetWindow()

Replace your existing handleSetWindow() with this:

void handleSetWindow() {
  if (server.hasArg("minutes")) {
    checkWindowMinutes = server.arg("minutes").toInt();
  }
  if (server.hasArg("repeat")) {
    repeatWindowMinutes = server.arg("repeat").toInt();
  }
  if (server.hasArg("caretaker")) {
    caretakerEnabled = server.arg("caretaker") == "1";
  }
  server.send(200, "text/plain",
    "Window: " + String(checkWindowMinutes) + "min | " +
    "Repeat: " + String(repeatWindowMinutes) + "min | " +
    "Caretaker: " + String(caretakerEnabled ? "ON" : "OFF"));
}

SECTION 7 — Update the setWindow() JavaScript function

Find function setWindow() in your HTML and replace it:

function setWindow() {
  const minutes   = document.getElementById('windowMinutes').value;
  const repeat    = document.getElementById('repeatMinutes').value;
  const caretaker = document.getElementById('caretaker').checked ? '1' : '0';
  fetch('/window?minutes=' + minutes + '&repeat=' + repeat + '&caretaker=' + caretaker)
    .then(r => r.text()).then(d => {
      document.getElementById('windowSetStatus').innerText = d;
    });
}

One thing to note: reminderCount is used in the reminder message but isn't declared in Section 2 above — add this line alongside the other new variables:

int  reminderCount = 0;

How it all flows together:

t=0      dispense → "Your pill is ready"
t=repeat → reminder → "X reminders left before caretaker notified"
t=repeat×2 → reminder again ...
t=15min  → caretaker → "not taken after 15 min"
t=20min  → caretaker → "still not taken after 20 min"
t=21min  → cycle ends
any time → pill taken → caretaker → "Pill taken at HH:MM" → cycle ends

The only thing you need to do outside of code is get the caretaker's Telegram chat ID — they message @userinfobot in Telegram and it replies with their ID. Drop that into caretakerChatID in Section 1.

multiple Telegram IDs

PRompt: Can i have two telegram ID's so i can work on this? or do i need to find someone else with a phone?

You can absolutely test with two IDs on your own phone — a few ways to do it:


Easiest — create a second Telegram account

Telegram allows a second account on the same phone. On Android and iPhone you can add a second account directly in the Telegram app settings. Use a different phone number (Google Voice works if you don't have a spare number).


Simpler — create a Telegram group

  • Create a group chat in Telegram
  • Add your bot to the group
  • Get the group's chat ID instead of a personal ID
  • Both "pill taker" and "caretaker" messages go to the same group but you can see them all in one place

To get a group chat ID, add @userinfobot to the group temporarily and it will report the group ID, then remove it.


Simplest for testing — just use your same chat ID for both

In Section 1 just set both to the same value:

const char* telegramChatID  = "8155911472";  // pill taker
const char* caretakerChatID = "8155911472";  // same for testing

You'll receive all messages — pill taker reminders and caretaker alerts — in the same chat. You can tell them apart by the message text. Once you're done testing swap in the real caretaker ID.

This is probably the fastest option since you don't need to do anything except copy your existing chat ID.

why verify

PRompt: why verify before uploading?

You're asking why verify/compile first before uploading to the ESP32 — a few good reasons:


It's faster

Compiling catches syntax errors, missing variables, undeclared functions, and type mismatches in seconds on your computer. Uploading takes 30-60 seconds plus a board reset. If there's a typo on line 47, you want to know before waiting a minute for the upload.


The ESP32 gives you nothing useful on a compile error

If bad code somehow uploads, the board either boots into garbage behavior, crashes silently, or just does nothing. The Arduino IDE error messages on compile are much clearer than trying to debug weird runtime behavior.


Protects the board during development

Every upload cycle writes to flash memory. Flash has a finite number of write cycles (typically 100,000+, so not a real concern in practice) but more importantly a failed or corrupted upload can occasionally leave the board in a bad state requiring a manual reset or reflash.


Catches the most common mistakes instantly

Things like: - Forgetting to declare reminderCount in Section 2 - Calling notifyCaretaker() before it's defined - A missing semicolon - Using a variable name that doesn't exist

All of these show up as compile errors with a line number — much easier to fix than chasing a bug at runtime.


The workflow is just: Verify first → fix any errors → then Upload. It saves time overall even though it feels like an extra step.5

notification changes

PRompt: modify the code so that the pill user only gets a count down to the notification of the caretaker for the first 15 minutes (not until the second caretaker notification. Notify both the pilltaker and the caretaker that the pill has been taken.

Here are the three specific changes to make:


Change 1 — Update the reminder message in checkPillWindow()

Find this block in your new checkPillWindow():

if (!alertSent && millis() - lastReminderTime > repeatMs) {
    if (elapsedMin < 21) {
      reminderCount++;
      int remindersLeft = max(0, (int)((21 - elapsedMin) / repeatWindowMinutes));
      String msg = "Reminder: your pill is waiting! (" +
                   String(remindersLeft) + " reminder(s) left before caretaker notified)";
      sendTelegram(msg);
      lastReminderTime = millis();
      Serial.println("Reminder sent. Count: " + String(reminderCount));
    }
  }

Replace with:

if (!alertSent && millis() - lastReminderTime > repeatMs) {
    if (elapsedMin < 21) {
      reminderCount++;
      String msg;
      if (elapsedMin < 15) {
        // Count down to caretaker notification
        int remindersLeft = max(0, (int)((15 - elapsedMin) / repeatWindowMinutes));
        msg = "Reminder: your pill is waiting! (" +
              String(remindersLeft) + " reminder(s) before caretaker is notified)";
      } else {
        // Past 15 min — no countdown, just remind
        msg = "Reminder: your pill is still waiting!";
      }
      sendTelegram(msg);
      lastReminderTime = millis();
      Serial.println("Reminder sent. Count: " + String(reminderCount));
    }
  }

Change 2 — Notify both when pill is taken

Find this block inside checkPillWindow():

if (isPillTaken()) {
      Serial.println("Pill taken! Notifying caretaker.");

      struct tm timeinfo;
      char timeStr[9] = "--:--";
      if (getLocalTime(&timeinfo)) {
        strftime(timeStr, sizeof(timeStr), "%H:%M", &timeinfo);
      }

      notifyCaretaker("Pill was taken at " + String(timeStr));
      resetPillCycle();
      return;
    }

Replace with:

if (isPillTaken()) {
      Serial.println("Pill taken! Notifying both.");

      struct tm timeinfo;
      char timeStr[9] = "--:--";
      if (getLocalTime(&timeinfo)) {
        strftime(timeStr, sizeof(timeStr), "%H:%M", &timeinfo);
      }

      sendTelegram("You took your pill at " + String(timeStr) + ". Great job!");
      notifyCaretaker("Pill was taken at " + String(timeStr));
      resetPillCycle();
      return;
    }

That's all two changes. The rest of the code stays exactly the same. The countdown now only appears in reminders sent before the 15-minute mark, and both the pill taker and caretaker get notified when the pill is taken.