circuitprofessor.com

path to learn electronics

Magical Dog Feeding with IoT Using Magicbit on the Cutting-Edge Magicblock Platform

ByMithila kumanjana

Jan 5, 2024
magicbit

In this project, I provide detailed information about the Magicbit board, the Magicblock platform, and the methodology employed for its execution. Mainly, 3 tasks are accomplished within this project: feeding food at specified times we define, playing the owner’s voice during feeding, and sending an automatic WhatsApp notification when the food basket is empty.

This is what my DIY project looks like. I used PVC pipes to create the structure and a plastic basket (dustbin). The Magicbit is the main controlling device.

Topic list (click to jump)

  1. What are Magicbit and Magic blocks (click)
  2. Components list(click)
  3. Making structure (click)
  4. Main task methodology (click)
    1. Server motor (click)
      • Magic block test
      • Arduino test
    2. Ultrasonic sensor (click)
      • Magic block test
      • Arduino test
    3. SD card reader (click)
      • Arduino test
  5. Final design (click)
    • Final code for magicbit (click)

What are Magicbit and Magic blocks

Magicbit is an innovative microcontroller platform designed for kids and researchers, with a focus on STEM education. The board is powered by ESP32, and it comes equipped with a variety of built-in sensors. Additionally, users have the flexibility to integrate third-party sensors. Magicbit offers firmware support, and its Magicblock platform provides straightforward methods for programming the Magicbit board.

(Note: In this project, tasks involving the server motor and ultrasonic sensor are developed using the MagicBlock platform. However, in more advanced tasks such as SPI communication and API connection, it proved to be a bit challenging. Consequently, I transitioned to the Arduino platform.)

Components list

ComponentsNo of componentsImage
Magicbit1magicbit
Servo Motor (MG996R)1MG996R
Ultrasonic sensor (HC-SR04 4Pin )1Ultrasonic sensor
SD card reader1SD card reader
Battery pack1battery pack
Speaker (Utilize your arrangement)1
PVC 1.5″4mpvc
PVC L socket 1.5″10L socket
PVC T socket 1.5″6T socket
PVC L socket 4″1L socket
Jumper wires12 (Depend on your sensors and actuators)jumper wires
Nuts and Bolts(3-8)nut and bolt
Super gluesuper glue
L bracket for Servo Motor1L bracket

(Note: You can customize these components according to your available resources and your requirements)

Making the structure

You need to cut all the pieces of PVC before attaching, and then your basket should be properly attached.

You can create your structure, and the ultrasonic sensor (measuring the emptiness of the food basket) and servo motor (opening and closing the food incoming gate) should be placed properly.

These constitute the primary components of the structure.

Main task methodology

The primary challenge in the methodology was the requirement for a specific timeframe to execute the tasks. There are two solutions to this issue. One involves utilizing external hardware clocks (Rtc_Pcf8563, DS3231, DS1307…), while the other entails utilizing servers such as NTP servers (pool.ntp.org, asia.pool.ntp.org, europe.pool.ntp.org, north-america.pool.ntp.org).

In this project, the chosen approach involved utilizing the pool.ntp.org NTP server connecting my Magicbit board to the internet using Wi-Fi, and then establishing a connection to an NTP server.

After obtaining the exact time, the server motor should operate according to the predefined schedule. I want to feed my dog at 8:30 a.m., 12:30 p.m., and 7:30 p.m.

Server motor

The server motor is the principal component that controls the incoming food. I want to open the gate at a specific time and keep it open until a specific amount comes out, then the gate should close. When opening the gate, the motor rotates 90 degrees, and when closing, it returns to 0 degrees.

Magic block test

Here are the magic block nodes

[{"id":"4c450fff.e432c","type":"tab","label":"Flow 2","disabled":false,"info":""},{"id":"6bf38739.36de28","type":"Servo","z":"4c450fff.e432c","name":"","epId":"id4240-1702186067376","pin":"26","x":930,"y":200,"wires":[]},{"id":"11fb8f2f.0f86e1","type":"inject","z":"4c450fff.e432c","name":"","topic":"LUNCH","payload":"true","payloadType":"bool","repeat":"","crontab":"17 15 * * *","once":false,"onceDelay":0.1,"x":160,"y":120,"wires":[["2f31391e.9212f6","d6c3a4d6.7330c8"]]},{"id":"2e34e8a9.4b8688","type":"inject","z":"4c450fff.e432c","name":"","topic":"early dinner","payload":"true","payloadType":"bool","repeat":"","crontab":"00 16 * * *","once":false,"onceDelay":0.1,"x":150,"y":160,"wires":[["2f31391e.9212f6","d6c3a4d6.7330c8"]]},{"id":"8fe52360.c4d0f","type":"inject","z":"4c450fff.e432c","name":"","topic":"","payload":"true","payloadType":"bool","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":170,"y":80,"wires":[["2f31391e.9212f6","d6c3a4d6.7330c8"]]},{"id":"2f31391e.9212f6","type":"DO","z":"4c450fff.e432c","name":"","epId":"id4240-1702186067376","pin":"16","x":590,"y":180,"wires":[]},{"id":"3ad2d334.d25f7c","type":"inject","z":"4c450fff.e432c","name":"","topic":"LUNCH","payload":"false","payloadType":"bool","repeat":"","crontab":"45 12 * * *","once":false,"onceDelay":0.1,"x":160,"y":280,"wires":[["2f31391e.9212f6","a69a3af6.2f5fc8"]]},{"id":"7a6f425.8282ebc","type":"inject","z":"4c450fff.e432c","name":"","topic":"early dinner","payload":"false","payloadType":"bool","repeat":"","crontab":"15 16 * * *","once":false,"onceDelay":0.1,"x":150,"y":320,"wires":[["2f31391e.9212f6","a69a3af6.2f5fc8"]]},{"id":"29f24029.e300c","type":"inject","z":"4c450fff.e432c","name":"","topic":"","payload":"false","payloadType":"bool","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":170,"y":240,"wires":[["2f31391e.9212f6","a69a3af6.2f5fc8"]]},{"id":"b7540d32.4a082","type":"inject","z":"4c450fff.e432c","name":"","topic":"breakfast","payload":"true","payloadType":"bool","repeat":"","crontab":"30 08 * * *","once":false,"onceDelay":0.1,"x":160,"y":200,"wires":[["2f31391e.9212f6","d6c3a4d6.7330c8"]]},{"id":"ab30d29.1f6b83","type":"inject","z":"4c450fff.e432c","name":"","topic":"breakfast","payload":"false","payloadType":"bool","repeat":"","crontab":"45 08 * * *","once":false,"onceDelay":0.1,"x":140,"y":360,"wires":[["2f31391e.9212f6","a69a3af6.2f5fc8"]]},{"id":"d6c3a4d6.7330c8","type":"switch","z":"4c450fff.e432c","name":"","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"1","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":630,"y":280,"wires":[["8cc06ff9.a6d34"]]},{"id":"a69a3af6.2f5fc8","type":"switch","z":"4c450fff.e432c","name":"","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"0","vt":"str"}],"checkall":"true","repair":false,"outputs":1,"x":630,"y":320,"wires":[["8cc06ff9.a6d34"]]},{"id":"8cc06ff9.a6d34","type":"function","z":"4c450fff.e432c","name":"","func":"// Function Node Code\n\n// Check the value of the boolean input\nif (msg.payload === true) {\n    // Bool value is true (1), send number 90 as output\n    msg.payload = 90;\n} else {\n    // Bool value is false (0), send number 0 as output\n    msg.payload = 0;\n}\n\nreturn msg;\n","outputs":1,"noerr":0,"x":800,"y":260,"wires":[["6bf38739.36de28","e6a3b99a.0d4f78"]]},{"id":"e6a3b99a.0d4f78","type":"debug","z":"4c450fff.e432c","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":930,"y":300,"wires":[]}]

copy this code and past your flow

After designing your magic block, proceed to deploy your model.

Arduino test

In this code, my server motor rotates 90 degrees and returns to 0 degrees at a specific time.

#include <WiFi.h>
#include "time.h"
#include <ESP32Servo.h>

const char* ssid = "<enter your ssid>";
const char* password = "<enter password>";
const char* ntpServer = "pool.ntp.org";
const long gmtOffset_sec = 5 * 3600 + 30 * 60; // Adjust according to your timezone
const int daylightOffset_sec = 0;

// Servo control variables
Servo MagicServo;

void setup() {
  Serial.begin(115200);

  // Connect to Wi-Fi
  Serial.printf("Connecting to %s ", ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println(" CONNECTED");

  // Initialize and get the time
  configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);

  // Attach the servo motor to GPIO 26 on ESP32
  MagicServo.attach(26);
}

void moveServo(int degrees) {
  MagicServo.write(degrees);
  delay(2000); // Adjust the delay as needed
}

void loop() {
  // Get current time
  struct tm timeinfo;
  if (!getLocalTime(&timeinfo)) {
    Serial.println("Failed to obtain time");
    return;
  }

  // Check if it's 8:30 AM
  if (timeinfo.tm_hour == 8 && timeinfo.tm_min == 30) {
    // Move the servo motor to the 90-degree position
    moveServo(90);
    Serial.println("servo works");
    delay(500);
    // Move the servo motor to the 0-degree position
    moveServo(0);
    delay(60000);
  }

  delay(1000); // Wait for 1 second before checking the time again
}

Ultrasonic sensor

Measuring whether the food basket is empty or not is the main task of this ultrasonic sensor. It depends on the height of your basket.

Magic block test

If you are employing Magicboard’s ultrasonic sensor, it will be more straightforward for you. However, in this project, I utilized third-party sensors and attempted to process data using magic blocks.

Here are the magic block nodes. (usual way)

[{"id":"14574eb7.8f3631","type":"US","z":"42a93cc6.1a7014","name":"","epId":"id4240-1702186067376","x":520,"y":280,"wires":[["7f2704ce.40845c"]]}]

(Little bit complicated way)

[{"id":"4e047f06.2da72","type":"tab","label":"Flow 4","disabled":false,"info":""},{"id":"ab4b3a29.21f1d8","type":"inject","z":"4e047f06.2da72","name":"","topic":"","payload":"1","payloadType":"num","repeat":"1","crontab":"","once":false,"onceDelay":"10","x":150,"y":180,"wires":[["9db492d7.8ab21","1f707715.605f69"]]},{"id":"2725b385.ade28c","type":"DI","z":"4e047f06.2da72","name":"echo","epId":"id4240-1702186067376","pin":"32","method":"0","x":610,"y":240,"wires":[["7f8047e8.5d9098","d9f95702.df24f8","831b53ef.8ad51"]]},{"id":"7f8047e8.5d9098","type":"function","z":"4e047f06.2da72","name":"distance","func":"\n// Get the pulse duration from the message payload\nvar duration = msg.payload;\n\n// Calculate the distance in centimeters\n// Speed of sound is approximately 343 meters/second\n// Distance = (Duration of pulse * Speed of sound) / 2\nvar distance = (duration * 343) / 20000;\n\n// Create a new message with the calculated distance\nmsg.payload = distance.toFixed(2); // Round to 2 decimal places\nmsg.topic = \"Distance\"; // Optionally set a topic for the message\n\n// Return the modified message\nreturn msg;\n","outputs":1,"noerr":0,"x":780,"y":280,"wires":[["fd7124bd.367ea8"]]},{"id":"d9f95702.df24f8","type":"debug","z":"4e047f06.2da72","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":770,"y":220,"wires":[]},{"id":"7a11d5b9.571acc","type":"DO","z":"4e047f06.2da72","name":"trigger","epId":"id4240-1702186067376","pin":"13","x":870,"y":160,"wires":[]},{"id":"fd7124bd.367ea8","type":"debug","z":"4e047f06.2da72","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":870,"y":360,"wires":[]},{"id":"857d51f.66774b","type":"debug","z":"4e047f06.2da72","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":670,"y":100,"wires":[]},{"id":"40f0ed6c.6bdbc4","type":"function","z":"4e047f06.2da72","name":"echo activating","func":"// Check if the input is the string \"3\"\nif (msg.payload === \"3\") {\n    // If the input is \"3\", send a message with payload \"1\"\n    var outputMsg = {\n        payload: \"1\"\n    };\n\n    // Return the message to be sent\n    return outputMsg;\n} else {\n    // If the input is not \"3\", send a message with payload \"0\"\n    var outputMsg = {\n        payload: \"0\"\n    };\n\n    // Return the message to be sent\n    return outputMsg;\n}\n","outputs":1,"noerr":0,"x":380,"y":220,"wires":[["88ac660b.4a4cf8","fc31f7a4.a1e928"]]},{"id":"adb5cdca.77ee2","type":"function","z":"4e047f06.2da72","name":"1to1 0,3to0","func":"// Check if the input is 1\nif (msg.payload === 1) {\n    // If the input is 1, send a message with payload \"1\"\n    return { payload: \"1\" };\n} else {\n    // If the input is not 1, send a message with payload \"0\"\n    return { payload: \"0\" };\n}\n","outputs":1,"noerr":0,"x":550,"y":160,"wires":[["857d51f.66774b","2b17d754.7bd008"]]},{"id":"fedb9227.3eac8","type":"debug","z":"4e047f06.2da72","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":590,"y":360,"wires":[]},{"id":"8fb48b8.2182578","type":"debug","z":"4e047f06.2da72","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":430,"y":100,"wires":[]},{"id":"9db492d7.8ab21","type":"debug","z":"4e047f06.2da72","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":170,"y":240,"wires":[]},{"id":"1f707715.605f69","type":"function","z":"4e047f06.2da72","name":"013 function","func":"// Initialize a flag to control the loop\nvar runFunction = true;\n\n// Check if the input is 1 and the flag is true\nif (msg.payload === 1 && runFunction) {\n    // Set the flag to false to prevent rechecking\n    runFunction = false;\n\n    // Function to be run continuously until 0 is received\n    function runContinuously() {\n        // Create a message with payload \"0\" for 2 milliseconds\n        var msg1 = {\n            payload: \"0\",\n            delay: 2\n        };\n\n        // Create a message with payload \"1\" for 10 milliseconds\n        var msg2 = {\n            payload: \"1\",\n            delay: 10\n        };\n\n        // Output \"0\"\n        node.send(msg1);\n\n        // Wait for 10 milliseconds and then output \"1\"\n        setTimeout(function () {\n            node.send(msg2);\n        }, 10);\n\n        // Wait for 500 milliseconds and then output \"3\"\n        setTimeout(function () {\n            var msg3 = {\n                payload: \"3\"\n            };\n            node.send(msg3);\n        }, 500);\n\n        // Check if the flag is still true, then run the function again\n        if (runFunction) {\n            setTimeout(runContinuously, 512); // Adjust the delay as needed\n        }\n    }\n\n    // Start the function\n    runContinuously();\n} else if (msg.payload === 0) {\n    // If the input is 0, set the flag to false to stop the function\n    runFunction = false;\n}\n\n// Pass through the input message unchanged\nreturn msg;\n","outputs":1,"noerr":0,"x":330,"y":180,"wires":[["8fb48b8.2182578","adb5cdca.77ee2","40f0ed6c.6bdbc4"]]},{"id":"2b17d754.7bd008","type":"function","z":"4e047f06.2da72","name":"string to num","func":"\n// Check if the input is a string containing \"1\"\nif (msg.payload === \"1\") {\n    // If the input is \"1,\" send a message with payload 1 (as a number)\n    return { payload: 1 };\n} else {\n    // If the input is not \"1,\" you can handle other cases or pass through the input unchanged\n    return msg;\n}\n","outputs":1,"noerr":0,"x":710,"y":160,"wires":[["7a11d5b9.571acc","a6d28b25.c39028"]]},{"id":"a6d28b25.c39028","type":"debug","z":"4e047f06.2da72","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":870,"y":100,"wires":[]},{"id":"88ac660b.4a4cf8","type":"function","z":"4e047f06.2da72","name":"string to number","func":"// Input value\nvar input = msg.payload;\n\n// Convert string to number\nvar output = Number(input);\n\n// Set the output\nmsg.payload = output;\n\nreturn msg;\n","outputs":1,"noerr":0,"x":400,"y":280,"wires":[["2725b385.ade28c","fedb9227.3eac8"]]},{"id":"fc31f7a4.a1e928","type":"debug","z":"4e047f06.2da72","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":550,"y":200,"wires":[]},{"id":"831b53ef.8ad51","type":"function","z":"4e047f06.2da72","name":"time period","func":"var startTime;\n\n// Check if the input is a logical high (assumed to be a boolean)\nif (msg.payload === true) {\n    // If the input becomes high, record the start time\n    startTime = process.hrtime();\n    context.set('startTime', startTime);\n} else if (msg.payload === false) {\n    // If the input becomes low, calculate the duration and output it\n    startTime = context.get('startTime');\n    if (startTime !== undefined) {\n        var endTime = process.hrtime(startTime);\n        var durationMicroseconds = (endTime[0] * 1e6) + (endTime[1] / 1e3);\n\n        // Set the duration as the output\n        msg.payload = durationMicroseconds;\n\n        // Reset the start time for the next high period\n        context.set('startTime', undefined);\n    }\n}\n\nreturn msg;\n","outputs":1,"noerr":0,"x":630,"y":440,"wires":[["aeba3ba3.728678"]]},{"id":"aeba3ba3.728678","type":"debug","z":"4e047f06.2da72","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":790,"y":440,"wires":[]}]
Arduino test

In Arduino implementation, I aim to send a WhatsApp message when the sensor values exceed 50 cm using Magicbit. I have employed a third-party WhatsApp bot and API to facilitate notifications. In this project, I aim to receive notifications before the owner leaves home. I inspect the food basket every day at 6:30 A.M. and subsequently send a WhatsApp notification before departure.

#include <WiFi.h>
#include <HTTPClient.h>
#include "time.h"

const char* ssid = "<enter your ssid>";            // Add your WiFi ssid
const char* password = "<enter your password>";         // Add your WiFi password

String apiKey = "<get epi key>";                  // Add your Token number that bot has sent you on WhatsApp messenger
String phone_number = "<your phone number>";      // Add your WhatsApp app registered phone number (same number that bot sent you in URL)

const int triggerPin = 21;  // Ultrasonic sensor trigger pin
const int echoPin = 32;     // Ultrasonic sensor echo pin

String url;  // URL String will be used to store the final generated URL

void setup() {
  Serial.begin(115200);

  // Connect to Wi-Fi
  WiFi.begin(ssid, password);
  Serial.println("Connecting to WiFi");

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println();
  Serial.println("Connected to the WiFi network");

  // Initialize and get the time from NTP server
  configTime(0, 0, "pool.ntp.org");
}

void loop() {
  // Get current time
  struct tm timeinfo;
  if (!getLocalTime(&timeinfo)) {
    Serial.println("Failed to obtain time");
    return;
  }

  // Check if it's 6:30 AM
  if (timeinfo.tm_hour == 6 && timeinfo.tm_min == 30) {
    // Read Ultrasonic distance
    float distance = getUltrasonicDistance();
    Serial.printf("Ultrasonic Distance: %.2f cm\n", distance);

    // Check if Ultrasonic distance is larger than 50 cm
    if (distance > 50.0) {
      Serial.println("Sending WhatsApp notification...");
      messageToWhatsApp("Refill the food basket");
      // Add a delay to prevent sending multiple notifications in a short time
      delay(30000);  // 30 seconds delay
    }
  }

  // Add a delay to check the conditions periodically
  delay(60000);  // Check every 1 minute
}

void messageToWhatsApp(String message) {
  // Adding all number, your API key, your message into one complete URL
  url = "https://api.callmebot.com/whatsapp.php?phone=" + phone_number + "&apikey=" + apiKey + "&text=" + urlencode(message);

  postData();  // Calling postData to run the above-generated URL once so that you will receive a message.
}

void postData() {
  int httpCode;     // Variable used to get the response HTTP code after calling API
  HTTPClient http;  // Declare object of class HTTPClient
  http.begin(url);  // Begin the HTTPClient object with the generated URL
  httpCode = http.POST(url);  // Finally, Post the URL with this function, and it will store the HTTP code

  if (httpCode == 200) {  // Check if the response HTTP code is 200
    Serial.println("Sent WhatsApp notification.");
  } else {  // If the response HTTP code is not 200, there is some error
    Serial.println("Error sending WhatsApp notification.");
  }

  http.end();  // After calling API end the HTTP client object.
}

String urlencode(String str) {
  String encodedString = "";
  char c;
  char code0;
  char code1;
  char code2;
  
  for (int i = 0; i < str.length(); i++) {
    c = str.charAt(i);
    
    if (c == ' ') {
      encodedString += '+';
    } else if (isalnum(c)) {
      encodedString += c;
    } else {
      code1 = (c & 0xf) + '0';
      
      if ((c & 0xf) > 9) {
        code1 = (c & 0xf) - 10 + 'A';
      }
      
      c = (c >> 4) & 0xf;
      code0 = c + '0';
      
      if (c > 9) {
        code0 = c - 10 + 'A';
      }
      
      code2 = '\0';
      encodedString += '%';
      encodedString += code0;
      encodedString += code1;
    }
    
    yield();
  }
  
  return encodedString;
}

float getUltrasonicDistance() {
  digitalWrite(triggerPin, LOW);
  delayMicroseconds(2);
  digitalWrite(triggerPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(triggerPin, LOW);
  
  float duration = pulseIn(echoPin, HIGH);
  return (duration * 0.0343) / 2;  // Calculate distance in cm
}

Note: You need to obtain the API to create that WhatsApp bot. Follow this tutorial for guidance on creating your WhatsApp bot.

Here, you must consistently connect your Magicbit board to the internet.

SD card reader

Here, you cannot upload your voice record to your Magicbit board. The Magicbit’s processors have 520 KiB, and typically, all microcontrollers utilize external storage devices to save files, such as sound files.

Here, I employ an external sound system, while you can opt for a single speaker with an amplification circuit, such as the PAM8403. Alternatively, you can utilize the MP3 DFplayer module independently.

I employed a separate Arduino Uno board for sound playback.

On the Magicbit code side, you only need to send the “up” test to the serial. This implies that when you want to play your voice recording, you have to input this command.

Serial.println("up");

This is Arduino side code

#include "SD.h"
#define SD_ChipSelectPin 4
#include "TMRpcm.h"
#include "SPI.h"

TMRpcm tmrpcm;

void setup() {
  tmrpcm.speakerPin = 9;
  Serial.begin(115200);

  if (!SD.begin(SD_ChipSelectPin)) {
    Serial.println("SD fail");
    return;
  }

  tmrpcm.setVolume(5);
   //tmrpcm.play("Teddy_me.wav");
}

void loop() {
  if (Serial.available() > 0) {
    String input = Serial.readStringUntil('\n');
    input.trim();

    if (input == "up") {
      Serial.println("Playing sound...");
      tmrpcm.play("Teddy_me.wav");
      delay(500); // Adjust delay as needed
    }
  }
}

Before uploading your voice to the SD card, you need to format your SD card. Additionally, ensure that your sound file is converted to 8-bit and that the format is changed to WAV type. use this site.

Final design

Before reaching this stage, you need to inspect all the components, and connections with Magicbit boards considering their durability. Additionally, consider food safety.

final code for magicbit.

#include <WiFi.h>
#include <HTTPClient.h>
#include "time.h"
#include <ESP32Servo.h>

const char* ssid = "<enter your ssid>";
const char* password = "<enter your password>";
const char* ntpServer = "pool.ntp.org";
const long gmtOffset_sec = 5 * 3600 + 30 * 60; // Adjust according to your timezone
const int daylightOffset_sec = 0;

// Servo control variables
Servo MagicServo;

// Flag to track if "up" has been sent
bool upSent = false;

const int triggerPin = 21;  // Ultrasonic sensor trigger pin
const int echoPin = 32;     // Ultrasonic sensor echo pin

String apiKey = "<get epi key>";                  // Add your Token number that bot has sent you on WhatsApp messenger
String phone_number = "<your phone number>";      // Add your WhatsApp app registered phone number (same number that bot sent you in URL)

String url;  // URL String will be used to store the final generated URL
float getUltrasonicDistance();
void messageToWhatsApp(String message);


void setup() {
  Serial.begin(115200);

  // Connect to Wi-Fi
  WiFi.begin(ssid, password);
  Serial.println("Connecting to WiFi");

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println();
  Serial.println("Connected to the WiFi network");

  // Initialize and get the time from NTP server
  configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);

  // Attach the servo motor to GPIO 26 on ESP32
  MagicServo.attach(26);
}

void moveServo(int degrees) {
  MagicServo.write(degrees);
  delay(500); // Adjust the delay as needed
}

void loop() {
  // Get current time
  struct tm timeinfo;
  if (!getLocalTime(&timeinfo)) {
    Serial.println("Failed to obtain time");
    return;
  }

  // Check if it's 6:30 AM
  if (timeinfo.tm_hour == 6 && timeinfo.tm_min == 30) {
    // Read Ultrasonic distance
    float distance1 = getUltrasonicDistance();
    delay(1000);  // Delay between samples
    float distance2 = getUltrasonicDistance();
    delay(1000);  // Delay between samples
    float distance3 = getUltrasonicDistance();

    // Calculate the median value
    float medianDistance = calculateMedian(distance1, distance2, distance3);

    Serial.printf("Ultrasonic Median Distance: %.2f cm\n", medianDistance);

    // Check if Ultrasonic distance is larger than 50 cm
    if (medianDistance > 50.0) {
      Serial.println("Sending WhatsApp notification...");
      messageToWhatsApp("Refill the food basket");
      // Add a delay to prevent sending multiple notifications in a short time
      delay(30000);  // 30 seconds delay
    }
  }

  // Check if it's 8:30 AM, 12:30 PM, or 7:30 PM
  if ((timeinfo.tm_hour == 8 && timeinfo.tm_min == 30) ||
      (timeinfo.tm_hour == 12 && timeinfo.tm_min == 30) ||
      (timeinfo.tm_hour == 19 && timeinfo.tm_min == 30)) {

    // Move the servo motor to the 90-degree position
    moveServo(90);
    Serial.println("servo works");
    Serial.println("up");
    delay(500); // Wait for 500 ms

    // Move the servo motor to the 0-degree position
    moveServo(0);
  }

  // Add a delay to check the conditions periodically
  delay(60000);  // Check every 1 minute
}

// ... (rest of the existing functions)

float calculateMedian(float a, float b, float c) {
  if ((a >= b && a <= c) || (a <= b && a >= c))
    return a;
  else if ((b >= a && b <= c) || (b <= a && b >= c))
    return b;
  else
    return c;
}

use the same Arduino code to play sounds

This marks the completion of the final project, allowing you to feed your dog even when you are away from home. For further development, you can monitor your dog using a camera module and implement the ESP32 CAM module with the Magicbit board. MicroPython serves as an alternative platform, and you can refer to the following links on the official Magicbit site for guidance.

3 thoughts on “Magical Dog Feeding with IoT Using Magicbit on the Cutting-Edge Magicblock Platform”
  1. of course like your website but you have to check the spelling on several of your posts. A number of them are rife with spelling issues and I in finding it very troublesome to inform the reality on the other hand I will certainly come back again.

  2. Somebody essentially lend a hand to make significantly posts I might state That is the very first time I frequented your web page and up to now I surprised with the research you made to create this particular put up amazing Excellent job

Leave a Reply

Your email address will not be published. Required fields are marked *