Track lateral and vertical movements of a 3D printer via AprilTags. Then, get informed of malfunctions related to motion via Telegram.
Supplies
1 x Raspberry Pi Pico
1 x WIZnet Ethernet HAT
1 x DFRobot HuskyLens AI Camera
1 x Raspberry Pi 3B+ or 4
1 x Creality CR-6 SE 3D Printer
1 x Keyes 10mm RGB LED Module (140C05)
1 x Buzzer
1 x LAN Ethernet Cable
1 x Xiaomi 20000 mAh 3 Pro Type-C Power Bank
1 x USB Buck-Boost Converter Board
2 x Mini Breadboard
1 x Jumper Wires
Step 1: Story
As you may have seen in my previous projects, I am utilizing a Creality CR-6 SE to print the 3D models I designed. During some of my prints, I encountered malfunctions related to the movements of my 3D printer four times while I was outside or not observing my printer. Generally, a non-watertight model, a problematic STL file, or an unstable printed part jammed or shifted one of the printer axes. When an axis is jammed or shifted, it causes faulty prints or impedes the printing process for troubleshooting.
Since the Creality CR-6 SE does not have Wi-Fi or an early warning system to monitor the printer motions and status, I decided to create a device to track the printer’s lateral and vertical motions while printing so as to get informed of potential malfunctions related to the printer movements:
- X-Axis (the hot end)
- Y-Axis (the printer or heated bed)
- Z-Axis
To endow this device with the ability to track X-axis, Y-axis, and Z-axis movements, I decided to employ the HuskyLens AI camera to recognize tags (AprilTags) denoting the axis motion. Since I was trying to develop a budget-friendly IoT device, I decided to utilize Raspberry Pi Pico to obtain and transfer the detected printer movements by the HuskyLens AI camera. Although there are various methods to connect Raspberry Pi Pico to the Internet, I chose to use the Pico-compatible WIZnet Ethernet HAT utilizing W5100S Hardwired TCP/IP CHIP. Since my printer is very close to my router, I did not encounter any issues regarding connecting the Ethernet cable or positioning the device.
After connecting Raspberry Pi Pico to the Internet, I decided to utilize a Telegram bot to track the detected printer axis movements and get notified of malfunctions related to the printer motions. Since Telegram is a cross-platform cloud-based messaging service compatible with iOS and Android, the Telegram bot allows the user to monitor the printer movements and potential malfunctions on several devices. On Telegram, it is effortless to create bots with a command list unalike any other messaging application, which are special accounts that do not require an additional phone number to set up.
To be able to process the detected printer movements and send updates to the Telegram bot automatically, I developed a PHP web application. The web application obtains the printer movements from the Raspberry Pi Pico via HTTP POST requests, stores the received printer movements in the given MySQL database table, detects potential malfunctions related to the printer motions, and sends updates (also notifications) to the Telegram bot via the Telegram Bot API. After developing the web application, I employed a Raspberry Pi 3 to host a LAMP web server to run the application.
Lastly, to make the device as stylish and robust as possible while operating in my workshop, I designed a T-800 Terminator-inspired case with a removable top cover (3D printable).
🎁🎨 Huge thanks to DFRobot for sending me the HuskyLens AI Camera.
🎁🎨 Huge thanks to WIZnet for providing me with a WIZnet Ethernet HAT.
🎁🎨 Also, huge thanks to Creality3D for sponsoring a Creality CR-6 SE 3D Printer.
🎁🎨 If you want to purchase some products from Creality3D, you can use my 10% discount coupon (Aktar10) even for their new and most popular printers: CR-10 Smart, CR-30 3DPrintMill, Ender-3 Pro, and Ender-3 V2. You can also use the coupon for Creality filaments.
Step 2: Designing and Printing a T-800 Terminator-inspired Case
Since I wanted to place the device towards my FDM 3D printer while printing 3D models in my workshop, I decided to design a complementing metallic case to create a robust and sturdy mechanism operating flawlessly. To make device connections more accessible, I added a removable top cover to the case. Then, I got inspired by The Terminator to add a T-800 replica to the device since it aims to track the movements of the printer and detect potential malfunctions to eliminate them 🙂
I designed the main case and its removable top cover in Autodesk Fusion 360. You can download their STL files below.
For the T-800 replica affixed to the removable top cover, I utilized this model from Thingiverse:
Then, I sliced all 3D models (STL files) in Ultimaker Cura.
Since I wanted to create a solid structure for the metallic case with the removable top cover and emphasize the T-800 theme, I utilized this PLA filament:
- eSilk Silver
Finally, I printed all parts (models) with my Creality CR-6 SE 3D Printer. Although I am a novice in 3D printing, and it is my first FDM 3D printer, I got incredible results effortlessly with the CR-6 SE 🙂
Step 3: Assembling the Case and Making Connections & Adjustments
First of all, I soldered female pin headers (11mm long legs) to the WIZnet Ethernet HAT and male pin headers to the Raspberry Pi Pico in order to connect them to the mini breadboard as shown below.
To recognize the learned tags (AprilTags) so as to track the printer motions (X-axis, Y-axis, and Z-axis), I connected the HuskyLens AI camera to the Raspberry Pi Pico by utilizing the I2C protocol. Also, I added a 10mm common anode RGB LED module (Keyes) and a buzzer to indicate the outcomes of operating functions.
Since the Raspberry Pi Pico operates at 3.3V, it is not able to power the HuskyLens AI camera and the RGB LED at the same time sufficiently. Therefore, I employed an external power source to supply the mentioned components: To elicit stable supply voltage, I connected a USB buck-boost converter board to a Xiaomi power bank.
After completing sensor connections and adjustments on mini breadboards successfully, I made the breadboard connection points rigid by utilizing a hot glue gun.
Then, after printing all 3D parts (models), I fastened all components to the main case and connected the LAN Ethernet cable from my router to the WIZnet Ethernet HAT.
Finally, I inserted the removable top cover in its slot on the main case and affixed the T-800 replica to the top of it via a hot glue gun. Also, I attached the HuskyLens AI camera to the case via a screw.
Step 4: Building a Telegram Bot With BotFather
I utilized BotFather to create a Telegram bot for this project. BotFather is an official Telegram bot that lets the user create and manage bots on Telegram without any coding required.
#️⃣ First of all, open BotFather on Telegram and enter /start to view the available command list and manuals.
#️⃣ Use the /newbot command to create a new bot. Enter the name of your bot when BotFather asks you for a name. It is displayed in contact details and elsewhere.
IoT 3D Printer Movement and Status Tracker
#️⃣ Then, enter the username of your bot. Usernames are 5-32 characters long and are case insensitive but may only include Latin characters, numbers, and underscores. They must end in ‘bot’, e.g. ‘tetris_bot’ or ‘TetrisBot’.
IoT_3D_printer_tracker_bot
#️⃣ After completing the steps above, BotFather generates an authorization token for your new bot. The authorization token is a string along the lines of 123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11 that is required to authorize the bot and send requests to the Telegram Bot API. Keep your token secure and store it safely.
#️⃣ Also, you can change your bot’s profile picture by using the /setuserpic command.
#️⃣ Finally, to add a description to your bot to be displayed when the chat is initialized, use the /setdescription command.
Since I wanted to get only printer movements updates from the Raspberry Pi Pico, I did not need to establish an SSL connection to set a webhook for the Telegram Bot API to send information back to the web application.
However, I needed to obtain the chat id to be able to send updates to the Telegram bot via the Telegram Bot API directly without establishing an SSL connection or setting up a webhook.
To get the chat id, I utilized the getUpdates method, which shows incoming updates using long polling and returns an array of Update objects.
#️⃣ Navigate to this web page by using your bot’s authorization token:
https://api.telegram.org/bot<_token_>/getUpdates
#️⃣ Then, send a message to your bot and refresh the page. It will display an object list, including the chat id:
chat ➡ id ➡ 1496498083
Step 5: Developing a Web Application in PHP to Send Updates to the Bot Via the Telegram Bot API
After creating my Telegram bot successfully, I decided to develop a web application in PHP, named telegram_3D_printer_bot, so as to receive printer movements from the Raspberry Pi Pico, process them to detect malfunctions, and send updates to the Telegram bot.
As shown below, the web application consists of one file and has four parameters to create the required database table and obtain printer movements:
- index.php
Parameters:
- x_axis
- y_axis
- z_axis
- create_table
To run all functions successfully, I created a class named IoT_3D_printer_tracker. You can download and inspect the index.php file below.
⭐ In the __init__ function, define the required variables to execute the functions:
- token ➡ authorization token of the given Telegram bot
- web_path ➡ Telegram Bot API request server
- conn ➡ MySQL database connection settings
- table ➡ MySQL database table name
⭐ In the send_message function, the application sends the given text to the given bot via the Telegram Bot API by employing the bot’s authorization token.
Syntax: https://api.telegram.org/bot<token>/sendMessage?chat_id=<chat_id>&text=<string>
⭐ In the send_photo function, the application sends the given picture to the given bot via the Telegram Bot API by employing the bot’s authorization token.
Syntax: https://api.telegram.org/bot<token>/sendPhoto?chat_id=<chat_id>&photo=<link>&caption=<string>
public function __init__($token, $server, $conn, $table){ $this->token = $token; $this->web_path = $server.$token; $this->conn = $conn; $this->table = $table; } // Telegram: public function send_message($chat_id, $string){ $new_message = $this->web_path."/sendMessage?chat_id=".$chat_id."&text=".urlencode($string); file_get_contents($new_message); } public function send_photo($chat_id, $photo, $caption){ $new_photo = $this->web_path."/sendPhoto?chat_id=".$chat_id."&photo=".$photo."&caption=".$caption; file_get_contents($new_photo); }
⭐ In the insert_new_data function, append the given printer movement information to the given database table.
⭐ In the get_data_from_database function, retrieve the latest row stored in the given database table.
⭐ In the database_create_table function, create the required database table and insert the default data items as the first row into the recently created table.
// Database -> Insert Data: public function insert_new_data($x_axis, $y_axis, $z_axis, $date){ $sql = "INSERT INTO `$this->table`(`x_axis`, `y_axis`, `z_axis`, `date`) VALUES ('$x_axis', '$y_axis', '$z_axis', '$date')"; mysqli_query($this->conn, $sql); } // Database -> Retrieve Data: public function get_data_from_database(){ $sql = "SELECT * FROM `$this->table` ORDER BY id DESC LIMIT 1"; $result = mysqli_query($this->conn, $sql); if($row = mysqli_fetch_assoc($result)){ return $row; } } // Database -> Create Table public function database_create_table(){ // Create a new database table. $sql_create = "CREATE TABLE `$this->table`( id int AUTO_INCREMENT PRIMARY KEY NOT NULL, x_axis varchar(255) NOT NULL, y_axis varchar(255) NOT NULL, z_axis varchar(255) NOT NULL, `date` varchar(255) NOT NULL );"; if(mysqli_query($this->conn, $sql_create)) echo("<br><br>Database Table Created Successfully!"); // Insert the default data items as the first row into the given database table. $sql_insert = "INSERT INTO `$this->table`(`x_axis`, `y_axis`, `z_axis`, `date`) VALUES ('0,0', '0,0', '0,0', 'default')"; if(mysqli_query($this->conn, $sql_insert)) echo("<br><br>Default Data Items Inserted Successfully!"); }
⭐ Define the required MySQL database connection settings for Raspberry Pi and the printer_tracker object with its required parameters.
$server = array( "name" => "localhost", "username" => "root", "password" => "bot", "database" => "telegram3dprinter", "table" => "entries" ); $conn = mysqli_connect($server["name"], $server["username"], $server["password"], $server["database"]); // Define the new 'printer_tracker' object: $printer_tracker = new IoT_3D_printer_tracker(); $bot_token = "<_____________________>"; // e.g., 123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11 $printer_tracker->__init__($bot_token, "https://api.telegram.org/bot", $conn, $server["table"]);
⭐ If the 3D printer’s lateral and vertical motions (X-axis, Y-axis, and Z-axis) are transferred by the Raspberry Pi Pico via an HTTP POST request:
⭐ Obtain the latest printer motions stored in the given database table.
⭐ Get the current printer motions transferred by the Raspberry Pi Pico.
⭐ Send the current printer motions to the Telegram bot, including the date and time, with the given chat id.
Go to Step 2 to learn how to elicit the chat id.
⭐ Compare the latest (previous) and current printer motions to detect potential malfunctions related to the printer’s lateral or vertical motion.
⭐ Then, inform the user of detected malfunctions, if any.
⭐ After every new update, send a schematic describing 3D printer motions to the Telegram bot.
⭐ Finally, append the current printer motions to the given database table.
if(isset($_POST["x_axis"]) && isset($_POST["y_axis"]) && isset($_POST["z_axis"])){ // Obtain the latest printer motions stored in the given database table. $row = $printer_tracker->get_data_from_database(); $previous_motions = [ "x_axis" => [intval(explode(",", $row["x_axis"])[0]), intval(explode(",", $row["x_axis"])[1])], "y_axis" => [intval(explode(",", $row["y_axis"])[0]), intval(explode(",", $row["y_axis"])[1])], "z_axis" => [intval(explode(",", $row["z_axis"])[0]), intval(explode(",", $row["z_axis"])[1])] ]; // Obtain the current printer motions transferred by Raspberry Pi Pico. $motions = [ "x_axis" => [intval(explode(",", $_POST["x_axis"])[0]), intval(explode(",", $_POST["x_axis"])[1])], "y_axis" => [intval(explode(",", $_POST["y_axis"])[0]), intval(explode(",", $_POST["y_axis"])[1])], "z_axis" => [intval(explode(",", $_POST["z_axis"])[0]), intval(explode(",", $_POST["z_axis"])[1])] ]; // Send the current printer motions to the given Telegram bot. $chat_id = ""; // 1496498083 $date = date("Y/m/d__h:i:s A"); $printer_tracker->send_message($chat_id, "📌 Recent Printer Movements:\n\n ⌛ $date\n\n ➡️ X-Axis: ".$_POST["x_axis"]."\n\n ⬇️ Y-Axis: ".$_POST["y_axis"]."\n\n ⬆️ Z-Axis: ".$_POST["z_axis"]); // Check for potential malfunctions related to the printer's lateral or vertical motion. if($previous_motions["x_axis"][0] == $motions["x_axis"][0] && $previous_motions["x_axis"][1] == $motions["x_axis"][1] && $previous_motions["y_axis"][0] == $motions["y_axis"][0] && $previous_motions["y_axis"][1] == $motions["y_axis"][1] && $previous_motions["z_axis"][0] == $motions["z_axis"][0] && $previous_motions["z_axis"][1] == $motions["z_axis"][1]){ $printer_tracker->send_message($chat_id, "⛔ 3D Printer is not working!"); }else{ if($previous_motions["x_axis"][0] == $motions["x_axis"][0] && $previous_motions["x_axis"][1] == $motions["x_axis"][1]){ $printer_tracker->send_message($chat_id, "⚠️ X-Axis: Potential Malfunction Detected!"); } if($previous_motions["y_axis"][0] == $motions["y_axis"][0] && $previous_motions["y_axis"][1] == $motions["y_axis"][1]){ $printer_tracker->send_message($chat_id, "⚠️ Y-Axis: Potential Malfunction Detected!"); } if($previous_motions["z_axis"][0] == $motions["z_axis"][0] && $previous_motions["z_axis"][1] == $motions["z_axis"][1]){ $printer_tracker->send_message($chat_id, "⚠️ Z-Axis: Potential Malfunction Detected!"); } } // Send a schematic describing 3D printer motions. $printer_tracker->send_photo($chat_id, "https://ars.els-cdn.com/content/image/3-s2.0-B9780128145647000031-f03-03-9780128145647.jpg", "3D Printer Motions"); // Save the current printer motions to the given database table. $printer_tracker->insert_new_data($_POST["x_axis"], $_POST["y_axis"], $_POST["z_axis"], $date); echo("Data Saved and Transferred to the Telegram Bot Successfully!"); }else{ echo("Waiting for new data..."); }
⭐ If requested, create the required database table (entries), including the default data items in the first row.
if(isset($_GET["create_table"]) && $_GET["create_table"] == "OK") $printer_tracker->database_create_table();
Step 6: Setting Up a LAMP Web Server on Raspberry Pi
Since I decided to host my web application on a Raspberry Pi 3, I needed to set up a LAMP web server.
#️⃣ First of all, open a terminal window by selecting Accessories ➡ Terminal from the menu.
#️⃣ Then, install the apache2 package by typing the following command into the terminal and pressing Enter:
sudo apt-get install apache2 -y
#️⃣ After installing the apache2 package successfully, open Chromium Web Browser and navigate to localhost so as to test the web server.
#️⃣ Then, enter the command below to the terminal to obtain the Raspberry Pi’s IP address:
hostname -I
#️⃣ To install the latest package versions successfully, update the Pi. Then, download the PHP package by entering these commands below to the terminal:
sudo apt-get update
sudo apt-get install php -y
#️⃣ To be able to send requests from the web server to the Telegram Bot API, install the php-curl package:
sudo apt-get install php-curl
#️⃣ Then, restart the apache server to activate the installed packages on the web server:
sudo service apache2 restart
Step 7: Creating a MySQL Database in MariaDB
Since I needed to log previous printer movements so as to detect potential malfunctions related to the printer’s lateral and vertical motions, I also set up a MariaDB server on the Raspberry Pi 3.
#️⃣ First of all, install the MariaDB (MySQL) server and PHP-MySQL packages by entering the following command into the terminal:
sudo apt-get install mariadb-server php-mysql -y
#️⃣ To create a new user, run the MySQL secure installation command in the terminal window:
sudo mysql_secure_installation
#️⃣ When requested, type the current password for the root user (enter for none). Then, press Enter.
#️⃣ Type in Y and press Enter to set the root password.
#️⃣ Type in bot at the New password: prompt, and press Enter.
#️⃣ Type in Y to remove anonymous users.
#️⃣ Type in Y to disallow root login remotely.
#️⃣ Type in Y to remove the test database and its access permissions.
#️⃣ Type in Y to reload privilege tables.
#️⃣ After successfully setting the MariaDB server, the terminal prints: All done! Thanks for using MariaDB!
#️⃣ Finally, to create a new database in the MariaDB server, run the MySQL interface in the terminal:
sudo mysql -uroot -p
#️⃣ Then, enter the recently changed root password – bot.
#️⃣ When the terminal shows the MariaDB [(none)]> prompt, create the new database (telegram3dprinter) by utilizing these commands below:
create database telegram3dprinter; GRANT ALL PRIVILEGES ON telegram3dprinter.* TO 'root'@'localhost' IDENTIFIED BY 'bot'; FLUSH PRIVILEGES;
#️⃣ Press Ctrl + D to exit the MariaDB [(none)]> prompt.
Step 8: Setting and Running the Web Application on Raspberry Pi
As discussed above, I set up a LAMP web server on my Raspberry Pi 3 to run the web application, but you can run it on any server as long as it is a PHP server.
#️⃣ First of all, install and extract the telegram_3D_printer_bot.zip folder.
#️⃣ Then, move the application folder (telegram_3D_printer_bot) to the Apache server (/var/www/html) by using the terminal since the Apache server is a protected location.
sudo mv /home/pi/Downloads/telegram_3D_printer_bot /var/www/html/
⭐ If the web application did not receive the printer movements from the Raspberry Pi Pico via an HTTP POST request, it prints: Waiting for new data…
⭐ Otherwise, the web application prints: Data Saved and Transferred to the Telegram Bot Successfully!
localhost/telegram_3D_printer_bot/
⭐ If the create_table parameter is set as OK, the web application creates the requested database table and inserts the default data items as the first row into the recently created table. Then, it prints:
Database Table Created Successfully!
Default Data Items Inserted Successfully!
localhost/telegram_3D_printer_bot/?create_table=OK
Step 9: Updating and Setting Up the HuskyLens AI Camera
HuskyLens AI camera is capable of transferring the detection results of its built-in functions via the UART (serial) or I2C protocol. I decided to utilize the I2C protocol since it is much easier to get information with the I2C protocol on the Raspberry Pi Pico.
However, before proceeding with the following steps, I needed to update the HuskyLens firmware with the latest version to get more accurate results.
#️⃣ After connecting the HuskyLens AI camera to the computer (Windows) via a micro USB cable, click the General Settings to view the version number.
#️⃣ Then, install the HuskyLens Uploader for Windows from here. If requested, you may need to install the CP2102N chip driver from here.
#️⃣ Search for the latest firmware version here, currently V0.5.3Alpha1, and download it.
#️⃣ Run the HuskyLens Uploader, a small black CMD window will pop up first, and after a while, the interface window will appear: Then, click the Select File button to upload the latest firmware file.
#️⃣ Finally, click the Upload button and wait about 5 minutes until the firmware is upgraded successfully.
After following the steps above, HuskyLens should display the upgraded version number on the settings menu.
Step 10: Detecting Tags (AprilTags) W/ HuskyLens
After upgrading the firmware, I trained HuskyLens to learn three different AprilTags from the figure below for each printer axis (X-axis, Y-axis, and Z-axis) so as to track the printer’s lateral and vertical motions.
#️⃣ First of all, switch to the Tag Recognition mode.
#️⃣ Since the default setting is to learn a single tag, long press the function button to open the parameter setting interface.
#️⃣ Then, switch to the Learn Multiple parameter and activate it.
#️⃣ Finally, save the settings and return to the Tag Recognition mode.
#️⃣ To learn a new tag (AprilTag), point the yellow + symbol to the selected tag and press the learning button. Then, release the learning button to complete learning the given tag.
#️⃣ To continue learning tags, short press the learning button before the countdown ends.
After training HuskyLens to learn tags (AprilTags) for each printer axis, I fastened each learned tag to my Creality CR-6 SE 3D printer’s X-axis, Y-axis, and Z-axis without blocking its fans, sensors, or belts.
Step 11: Setting Up Raspberry Pi Pico
Since I decided to program the Raspberry Pi Pico on Thonny, I needed to configure some settings and install MicroPython for Raspberry Pi Pico on Thonny.
#️⃣ First of all, download the latest Thonny IDE version, compatible with Windows, macOS, and Linux. If you are programming the Pico on Raspberry Pi, Thonny is already installed on the Raspberry Pi OS.
#️⃣ Then, find the BOOTSEL button on your Raspberry Pi Pico.
#️⃣ Press the BOOTSEL button and hold it while connecting the other end of the micro USB cable to your computer.
#️⃣ This puts the Raspberry Pi Pico into the USB mass storage device mode (Bootloader Mode).
#️⃣ Thonny should automatically open the installation screen. If not, at the bottom right-hand corner of the window, click on the Python version, choose Configure interpreter…, and select MicroPython (Raspberry Pi Pico).
#️⃣ Then, on the Interpreter Tab screen, click the Install or update firmware link.
#️⃣ When the dialog box pops up, click the Install button to copy the latest version of the MicroPython firmware to the Raspberry Pi Pico.
#️⃣ Wait for the installation to complete and click Close.
After completing the steps above, Thonny should recognize the board and firmware automatically when plugging the Raspberry Pi Pico to the computer without pressing the BOOTSEL button.
Step 12: Modifying CircuitPython Libraries for Raspberry Pi Pico and the WIZnet Ethernet HAT
Since the WIZnet Ethernet HAT requires Adafruit CircuitPython libraries, I needed to set and configure CircuitPython on Thonny. Plausibly, Thonny has a built-in file transfer feature that makes copying files and libraries to the Raspberry Pi Pico very easy.
However, while working on this project, I encountered many bugs, errors, and incompatibilities regarding CircuitPython libraries. Therefore, I had to modify nearly all libraries and files to rectify the occurring issues and bugs.
Below, I will explain how to set CircuitPython and fix the mentioned issues and bugs step by step. But, you can download the lib.zip folder and copy the lib folder directly to your Raspberry Pi Pico without replicating the steps below.
#️⃣ First of all, download the latest versions of Blinka and PlatformDetect modules to run CircuitPython libraries on MicroPython.
#️⃣ To install the PlatformDetect module to the Raspberry Pi Pico, copy the adafruit_platformdetect folder and upload it to the Pico under the lib folder.
#️⃣ To install the Blinka module to the Raspberry Pi Pico, copy everything inside the src folder and upload those files to the Pico under the lib folder.
#️⃣ To free up some space, remove the unnecessary files under the adafruit_blinka folder before uploading modules to the Pico.
#️⃣ To run the WIZnet Ethernet HAT, download the adafruit_bus_device and adafruit_wiznet5k libraries. Then, upload these libraries to the Pico under the lib folder.
#️⃣ If Thonny throws attribute errors like below, change the monotonic function with the ticks_cpu function in all files under the adafruit_wiznet5k folder.
Traceback (most recent call last): File "", line 49, in File "/lib/adafruit_wiznet5k/adafruit_wiznet5k.py", line 188, in __init__ AttributeError: 'module' object has no attribute 'monotonic' Traceback (most recent call last): File "<stdin>", line 55, in <module> File "/lib/adafruit_wiznet5k/adafruit_wiznet5k.py", line 199, in __init__ File "/lib/adafruit_wiznet5k/adafruit_wiznet5k.py", line 219, in set_dhcp File "/lib/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py", line 491, in request_dhcp_lease File "/lib/adafruit_wiznet5k/adafruit_wiznet5k_dhcp.py", line 363, in _dhcp_state_machine AttributeError: 'module' object has no attribute 'monotonic'
#️⃣ If Thonny throws an error regarding allocation, set the MAX_PACKET bytes to 3500 or lower in the adafruit_wiznet5k.py file under the adafruit_wiznet5k folder.
Traceback (most recent call last): File "<stdin>", line 50, in <module> File "/lib/adafruit_wiznet5k/adafruit_wiznet5k.py", line 176, in __init__ MemoryError: memory allocation failed, allocating 4000 bytes
#️⃣ To make HTTP requests to a given web application with the WIZnet Ethernet HAT, download the adafruit_requests library. Then, upload the adafruit_requests.py file to the Pico under the lib folder.
Since the latest versions of the adafruit_requests library are not working with the adafruit_wiznet5k library, I utilized the 1.8.1 version in this project.
Also, I needed to modify the adafruit_requests.py file to disable checking for the server response since it causes timeout errors on Thonny.
After configuring module folders and modifying library files, I saved all required folders and files under the lib folder. Then, I uploaded the lib folder to the Raspberry Pi Pico via the built-in file transfer feature on Thonny.
Step 13: Reading Results Generated by HuskyLens With Raspberry Pi Pico
Since HuskyLens does not provide an official library for the Raspberry Pi Pico, I modified these functions to obtain tag recognition results from HuskyLens via the I2C protocol.
After modifying the functions, I uploaded them under the huskylib.py file to the Pico.
Then, I was able to get the learned blocks with IDs generated by HuskyLens for each detected tag (AprilTag) and process them to derive tag coordinates (tag positions).
#️⃣ When saving code files to the Raspberry Pi Pico, click the Save button and choose the Raspberry Pi Pico option on Thonny.
Step 14: Programming Raspberry Pi Pico
After uploading all required modules and libraries, I saved the following code in the main.py file to run it automatically when the Raspberry Pi Pico is powered up.
⭐ Include the required modules and libraries:
import board import busio import digitalio import time import adafruit_requests as requests from adafruit_wiznet5k.adafruit_wiznet5k import WIZNET5K import adafruit_wiznet5k.adafruit_wiznet5k_socket as socket from huskylib import HuskyLensLibrary from time import sleep from machine import Pin, PWM
⭐ Define the IoT_3D_printer_tracker class and its functions.
⭐ In the __init__ function:
⭐ Define the PHP web application path.
⭐ Setup the network configuration settings with the WIZnet Ethernet HAT’s default MAC address:
0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED
⭐ Define the WIZnet Ethernet HAT pin settings.
⭐ Reset the WIZnet Ethernet HAT.
⭐ Initialize the Ethernet interface without DHCP.
⭐ Set the network configuration.
⭐ Print the WIZnet Ethernet HAT information.
⭐ Define the HuskyLens AI camera settings to activate the I2C protocol.
def __init__(self, SPI0_SCK, SPI0_TX, SPI0_RX, SPI0_CSn, W5x00_RSTn): # Define the PHP web application path. self.App = "http://192.168.1.20/telegram_3D_printer_bot/" # Setup the network configuration settings: MY_MAC = (0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED) IP_ADDRESS = (192, 168, 1, 24) SUBNET_MASK = (255, 255, 255, 0) GATEWAY_ADDRESS = (192, 168, 1, 1) DNS_SERVER = (8, 8, 8, 8) # Define the WIZnet Ethernet HAT pin settings: ethernetRst = digitalio.DigitalInOut(W5x00_RSTn) ethernetRst.direction = digitalio.Direction.OUTPUT cs = digitalio.DigitalInOut(SPI0_CSn) spi_bus = busio.SPI(SPI0_SCK, MOSI=SPI0_TX, MISO=SPI0_RX) # Reset the WIZnet Ethernet HAT. ethernetRst.value = False sleep(1) ethernetRst.value = True # Initialize the Ethernet interface without DHCP. self.eth = WIZNET5K(spi_bus, cs, is_dhcp=False, mac=MY_MAC, debug=False) # Set the network configuration. self.eth.ifconfig = (IP_ADDRESS, SUBNET_MASK, GATEWAY_ADDRESS, DNS_SERVER) # Optional: Initialize the Ethernet interface with DHCP. #eth = WIZNET5K(spi_bus, cs, is_dhcp=True, mac=MY_MAC, debug=False) # Print the WIZnet Ethernet HAT information. print("Chip Version: ", self.eth.chip) print("\nMAC Address: ", [hex(i) for i in self.eth.mac_address]) print() # Define HuskyLens settings. self.husky_lens = HuskyLensLibrary("I2C", Pin(4), Pin(5), 0x32) # Define the RGB LED settings: self.red = PWM(Pin(10)) self.green = PWM(Pin(11)) self.blue = PWM(Pin(12)) self.red.freq(1000) self.green.freq(1000) self.blue.freq(1000) # Define the buzzer: self.buzzer = PWM(Pin(13)) self.buzzer.freq(450) self.adjust_color(0, 0, 65025)
⭐ In the detect_and_transfer_motions function:
⭐ If HuskyLens recognizes three tags (AprilTags) for each printer axis in the same frame, obtain tag coordinates (positions) for X-axis, Y-axis, and Z-axis.
⭐ Define the data parameters to transfer printer movements (detected tag positions) to the given web application.
⭐ Initialize a requests object with the Ethernet interface and a socket.
⭐ Then, make an HTTP POST request with the data parameters to the web application.
⭐ After sending the current printer motions successfully, notify the user via the RGB LED and the buzzer.
⭐ Maintain the DHCP lease.
def detect_and_transfer_motions(self, rest): detected_tag = self.husky_lens.command_request_blocks_learned() tag_number = len(detected_tag) if (tag_number == 3): self.adjust_color(65025, 65025, 0) x_axis = str(detected_tag[0][0]) + "," + str(detected_tag[0][1]) y_axis = str(detected_tag[1][0]) + "," + str(detected_tag[1][1]) z_axis = str(detected_tag[2][0]) + "," + str(detected_tag[2][1]) print() print("X-Axis: " + x_axis) print("Y-Axis: " + y_axis) print("Z-Axis: " + z_axis) # Define the parameters to transfer printer movements to the web application. parameters = {"x_axis" : x_axis, "y_axis" : y_axis, "z_axis" : z_axis} # Initialize a requests object with the Ethernet interface and a socket. requests.set_socket(socket, self.eth) print() # Make an HTTP POST request to the web application. r = requests.post(self.App, data=parameters) print("-" * 60) print("Data Saved and Transferred to the Telegram Bot Successfully!") print("-" * 60) r.close() # Notify the user after sending the current printer motions. self.adjust_color(0, 65025, 0) self.buzzer.duty_u16(65025) sleep(5) self.buzzer.duty_u16(0) self.adjust_color(65025, 0, 65025) else: print(detected_tag) # Maintain the DHCP lease. self.eth.maintain_dhcp_lease() sleep(rest)
⭐ In the adjust_color function, change the RGB LED color by using the PWM frequency (0 – 65025).
def adjust_color(self, red_x, green_x, blue_x): self.red.duty_u16(65025-red_x) self.green.duty_u16(65025-green_x) self.blue.duty_u16(65025-blue_x)
⭐ Define the tracker class object with the WIZnet Ethernet HAT pins.
tracker = IoT_3D_printer_tracker(board.GP18, board.GP19, board.GP16, board.GP17, board.GP15) while True: tracker.detect_and_transfer_motions(30)
Step 15: Modes and Features
🌐🤖 If the WIZnet Ethernet HAT connects to the router via the Ethernet interface without DHCP, the device turns the RGB LED to blue.
🌐🤖 If HuskyLens recognizes three tags (AprilTags) for each printer axis in the same frame, the device turns the RGB LED to yellow.
🌐🤖 Then, if the WIZnet Ethernet HAT transfers tag coordinates (positions) for X-axis, Y-axis, and Z-axis to the PHP web application via an HTTP POST request successfully, the device turns the RGB LED to green and activates the buzzer to notify the user.
🌐🤖 After completing the first HTTP POST request successfully, the device turns the RGB LED to magenta as the default color.
🌐🤖 When the web application receives the current printer movements (tag positions), it compares them to the previous printer movements stored in the database table to detect potential malfunctions related to the printer motions.
🌐🤖 Then, the application sends the current printer movements with a schematic describing 3D printer motions to the given Telegram bot via the Telegram Bot API.
🌐🤖 If the web application detects potential malfunctions on X-axis, Y-axis, or Z-axis, it adds notification messages to the transferred update up to two axes.
⚠️ X-Axis: Potential Malfunction Detected!
⚠️ Y-Axis: Potential Malfunction Detected!
⚠️ Z-Axis: Potential Malfunction Detected!
🌐🤖 If the web application detects malfunctions on three axes, it adds this notification message to the transferred update:
⛔ 3D Printer is not working!
🌐🤖 Also, the device prints notifications and the detected printer movements (tag positions) on the shell for debugging.
As far as my experiments go, the device works impeccably while tracking the printer movements and informing me of potential malfunctions related to the printer motions 🙂
Step 16: Videos and Conclusion
Step 17: Schematics
Step 18: Code and Downloads
Source: AI-driven IoT 3D Printer Motion & Status Tracker W/ Telegram