Sitemap / Advertise

Introduction

Track lateral and vertical movements of a 3D printer via AprilTags. Then, get informed of malfunctions related to motion via Telegram.


Tags

Share

AI-driven IoT 3D Printer Motion & Status Tracker w/ Telegram

Advertisement:


read_later

Read Later



read_later

Read Later

Introduction

Track lateral and vertical movements of a 3D printer via AprilTags. Then, get informed of malfunctions related to motion via Telegram.

Tags

Share





Advertisement

Advertisement




    Components :
  • [1]Raspberry Pi Pico
  • [1]WIZnet Ethernet HAT
  • [1]DFRobot HuskyLens AI Camera
  • [1]Raspberry Pi 3B+ or 4
  • [1]Creality CR-6 SE 3D Printer
  • [1]Keyes 10mm RGB LED Module (140C05)
  • [1]Buzzer
  • [1]LAN Ethernet Cable
  • [1]Xiaomi 20000 mAh 3 Pro Type-C Power Bank
  • [1]USB Buck-Boost Converter Board
  • [2]Mini Breadboard
  • [1]Jumper Wires

Description

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:

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.

project-image
Figure - 76.1


project-image
Figure - 76.2


project-image
Figure - 76.3


project-image
Figure - 76.4

Step 1: 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.

project-image
Figure - 76.5


project-image
Figure - 76.6


project-image
Figure - 76.7

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.

project-image
Figure - 76.8


project-image
Figure - 76.9


project-image
Figure - 76.10

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:

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 :)

project-image
Figure - 76.11

Step 1.1: 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.

project-image
Figure - 76.12


project-image
Figure - 76.13

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.

project-image
Figure - 76.14

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.

project-image
Figure - 76.15


project-image
Figure - 76.16


project-image
Figure - 76.17


project-image
Figure - 76.18


project-image
Figure - 76.19


project-image
Figure - 76.20

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.

project-image
Figure - 76.21


project-image
Figure - 76.22


project-image
Figure - 76.23


project-image
Figure - 76.24

Step 2: 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.

project-image
Figure - 76.25

#️⃣ 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.

project-image
Figure - 76.26

#️⃣ Also, you can change your bot's profile picture by using the /setuserpic command.

project-image
Figure - 76.27

#️⃣ Finally, to add a description to your bot to be displayed when the chat is initialized, use the /setdescription command.

project-image
Figure - 76.28


project-image
Figure - 76.29

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

project-image
Figure - 76.30

Step 3: 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:

Parameters:

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:

⭐ 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();

project-image
Figure - 76.31


project-image
Figure - 76.32


project-image
Figure - 76.33

Step 4: 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

project-image
Figure - 76.34

#️⃣ 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

project-image
Figure - 76.35


project-image
Figure - 76.36

#️⃣ 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

project-image
Figure - 76.37

#️⃣ 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

project-image
Figure - 76.38

Step 4.1: 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

project-image
Figure - 76.39

#️⃣ 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!

project-image
Figure - 76.40

#️⃣ 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.

project-image
Figure - 76.41

Step 4.2: 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.

project-image
Figure - 76.42

#️⃣ 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/

project-image
Figure - 76.43

⭐ 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/

project-image
Figure - 76.44

⭐ 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

project-image
Figure - 76.45

Step 5: 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.

project-image
Figure - 76.46


project-image
Figure - 76.47

#️⃣ 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.

project-image
Figure - 76.48

#️⃣ 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.

project-image
Figure - 76.49

#️⃣ Finally, click the Upload button and wait about 5 minutes until the firmware is upgraded successfully.

project-image
Figure - 76.50

After following the steps above, HuskyLens should display the upgraded version number on the settings menu.

project-image
Figure - 76.51

Step 5.1: 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.

project-image
Figure - 76.52

#️⃣ First of all, switch to the Tag Recognition mode.

project-image
Figure - 76.53

#️⃣ 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.

project-image
Figure - 76.54

#️⃣ Finally, save the settings and return to the Tag Recognition mode.

project-image
Figure - 76.55

#️⃣ 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.

project-image
Figure - 76.56


project-image
Figure - 76.57

#️⃣ To continue learning tags, short press the learning button before the countdown ends.

project-image
Figure - 76.58


project-image
Figure - 76.59

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.

project-image
Figure - 76.60


project-image
Figure - 76.61

Step 6: 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.

project-image
Figure - 76.62

#️⃣ 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.

project-image
Figure - 76.63

#️⃣ When the dialog box pops up, click the Install button to copy the latest version of the MicroPython firmware to the Raspberry Pi Pico.

project-image
Figure - 76.64

#️⃣ 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 6.1: 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.

project-image
Figure - 76.65


project-image
Figure - 76.66

#️⃣ 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'

project-image
Figure - 76.67

#️⃣ 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

project-image
Figure - 76.68

#️⃣ 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.

project-image
Figure - 76.69

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.

project-image
Figure - 76.70


project-image
Figure - 76.71


project-image
Figure - 76.72

Step 6.2: 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.

project-image
Figure - 76.73


project-image
Figure - 76.74


project-image
Figure - 76.75

Step 7: 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)

project-image
Figure - 76.76


project-image
Figure - 76.77


project-image
Figure - 76.78

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.

project-image
Figure - 76.79

🌐🤖 If HuskyLens recognizes three tags (AprilTags) for each printer axis in the same frame, the device turns the RGB LED to yellow.

project-image
Figure - 76.80


project-image
Figure - 76.81


project-image
Figure - 76.82


project-image
Figure - 76.83


project-image
Figure - 76.84

🌐🤖 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.

project-image
Figure - 76.85

🌐🤖 After completing the first HTTP POST request successfully, the device turns the RGB LED to magenta as the default color.

project-image
Figure - 76.86

🌐🤖 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.

project-image
Figure - 76.87

🌐🤖 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!

project-image
Figure - 76.88


project-image
Figure - 76.89


project-image
Figure - 76.90


project-image
Figure - 76.91


project-image
Figure - 76.92

🌐🤖 If the web application detects malfunctions on three axes, it adds this notification message to the transferred update:

⛔ 3D Printer is not working!

project-image
Figure - 76.93

🌐🤖 Also, the device prints notifications and the detected printer movements (tag positions) on the shell for debugging.

project-image
Figure - 76.94

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 :)

project-image
Figure - 76.95


project-image
Figure - 76.96

Videos and Conclusion


Code

main.py

Download



# AI-driven IoT 3D Printer Motion & Status Tracker w/ Telegram

# Windows, Linux, or Ubuntu

# By Kutluhan Aktar

# Track lateral and vertical movements of a 3D printer via AprilTags. Then, get informed of malfunctions related to motion via Telegram.

# For more information:
# https://www.theamplituhedron.com/projects/IoT_3D_Printer_Motion_Status_Tracker_w_Telegram/

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

class IoT_3D_printer_tracker:
    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)
    # Detect the 3D printer lateral and vertical motions via tags (AprilTags). Then, transfer the current printer motions to the given web application.
    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)
    # Change the RGB LED's color. 
    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 new 'tracker' class object.
tracker = IoT_3D_printer_tracker(board.GP18, board.GP19, board.GP16, board.GP17, board.GP15)
        
while True:
    tracker.detect_and_transfer_motions(30)


index.php

Download



<?php

// Define the IoT_3D_printer_tracker class and its functions:
class IoT_3D_printer_tracker {
	public $token, $web_path, $conn, $table;
	
	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);
	}
	
    // 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 database and server settings:
$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"]); 


// Get updates on the 3D printer's lateral and vertical motions (X-axis, Y-axis, and Z-axis) from Raspberry Pi Pico so as to detect potential malfunctions.
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 a new database table, including the default data items in the first row.
if(isset($_GET["create_table"]) && $_GET["create_table"] == "OK") $printer_tracker->database_create_table();

?>


huskylib.py

Download



#Library credits: RRoy  https://community.dfrobot.com/makelog-310469.html
 
import ubinascii
import time
from machine import UART,I2C,Pin
 
commandHeaderAndAddress = "55AA11"
algorthimsByteID = {
    "ALGORITHM_OBJECT_TRACKING": "0100",
    "ALGORITHM_FACE_RECOGNITION": "0000",
    "ALGORITHM_OBJECT_RECOGNITION": "0200",
    "ALGORITHM_LINE_TRACKING": "0300",
    "ALGORITHM_COLOR_RECOGNITION": "0400",
    "ALGORITHM_TAG_RECOGNITION": "0500",
    "ALGORITHM_OBJECT_CLASSIFICATION": "0600"
}
 
COMMAND_REQUEST_CUSTOMNAMES= 0x2f
COMMAND_REQUEST_TAKE_PHOTO_TO_SD_CARD = 0x30
COMMAND_REQUEST_SAVE_MODEL_TO_SD_CARD = 0x32
COMMAND_REQUEST_LOAD_MODEL_FROM_SD_CARD = 0x33
COMMAND_REQUEST_CUSTOM_TEXT = 0x34
COMMAND_REQUEST_CLEAR_TEXT = 0x35
COMMAND_REQUEST_LEARN_ONECE = 0x36
COMMAND_REQUEST_FORGET = 0x37
COMMAND_REQUEST_SCREENSHOT_TO_SD_CARD = 0x39
COMMAND_REQUEST_FIRMWARE_VERSION = 0x3C
 
class HuskyLensLibrary:
    def __init__(self, proto, SDA, SCL, address):
        self.proto=proto
        self.address=address
        if(self.proto=="SERIAL"):
            self.huskylensSer = UART(2,baudrate=9600,rx=33,tx=32,timeout=100)
        else :
            self.huskylensSer = I2C(0, scl=SCL, sda=SDA, freq=100000)
        self.lastCmdSent = ""
 
    def writeToHuskyLens(self, cmd):
        self.lastCmdSent = cmd
        if(self.proto=="SERIAL"):
            self.huskylensSer.write(cmd)
        else :
            self.huskylensSer.writeto(self.address, cmd)
 
    def calculateChecksum(self, hexStr):
        total = 0
        for i in range(0, len(hexStr), 2):
            total += int(hexStr[i:i+2], 16)
        hexStr = hex(total)[-2:]
        return hexStr
 
    def cmdToBytes(self, cmd):
        return ubinascii.unhexlify(cmd)
 
    def splitCommandToParts(self, str):
        headers = str[0:4]
        address = str[4:6]
        data_length = int(str[6:8], 16)
        command = str[8:10]
        if(data_length > 0):
            data = str[10:10+data_length*2]
        else:
            data = []
        checkSum = str[2*(6+data_length-1):2*(6+data_length-1)+2]
        return [headers, address, data_length, command, data, checkSum]
 
    def getBlockOrArrowCommand(self):
        if(self.proto=="SERIAL"):
                    byteString = self.huskylensSer.read(5)
                    byteString += self.huskylensSer.read(int(byteString[3]))
                    byteString += self.huskylensSer.read(1)
        else:
                    byteString  =self.huskylensSer.readfrom(self.address,5)
                    ##print("______")
                    ##print(byteString)
                    ##print(byteString[3])
                    byteString +=self.huskylensSer.readfrom(self.address,byteString[3]+1)
                    ##print("=======")
                    ##print(byteString)
        commandSplit = self.splitCommandToParts(''.join(['%02x' % b for b in byteString]))
        return commandSplit[4]
 
    def processReturnData(self):
        inProduction = True
        if(inProduction):
            try:
                if(self.proto=="SERIAL"):
                    byteString = self.huskylensSer.read(5)
                    byteString += self.huskylensSer.read(int(byteString[3]))
                    byteString += self.huskylensSer.read(1)
                else:
                    byteString  =self.huskylensSer.readfrom(self.address,5)
                    ##print("______")
                    ##print(byteString)
                    ##print(byteString[3])
                    byteString +=self.huskylensSer.readfrom(self.address,byteString[3]+1)
                    ##print("=======")
                    ##print(byteString)
                commandSplit = self.splitCommandToParts(''.join(['%02x' % b for b in byteString]))
                if(commandSplit[3] == "2e"):
                    return "Knock Recieved"
                else:
                    returnData = []
                    numberOfBlocksOrArrow = int(
                        commandSplit[4][2:4]+commandSplit[4][0:2], 16)
                    numberOfIDLearned = int(
                        commandSplit[4][6:8]+commandSplit[4][4:6], 16)
                    frameNumber = int(
                        commandSplit[4][10:12]+commandSplit[4][8:10], 16)
                    for i in range(numberOfBlocksOrArrow):
                        returnData.append(self.getBlockOrArrowCommand())
                    finalData=[]
                    tmp=[]
                    for i in returnData:
                        tmp=[]
                        for q in range(0,len(i),4):
                            tmp.append(int(i[q:q+2],16)+int(i[q+2:q+4],16))
                        finalData.append(tmp)
                        tmp=[]
                    return finalData
            except:
                 print("Read error")
                 return []
                
    def command_request_knock(self):
        cmd = self.cmdToBytes(commandHeaderAndAddress+"002c3c")
        self.writeToHuskyLens(cmd)
        return self.processReturnData()
 
    def command_request(self):
        cmd = self.cmdToBytes(commandHeaderAndAddress+"002030")
        self.writeToHuskyLens(cmd)
        return self.processReturnData()
 
    def command_request_blocks(self):
        cmd = self.cmdToBytes(commandHeaderAndAddress+"002131")
        self.writeToHuskyLens(cmd)
        return self.processReturnData()
 
    def command_request_arrows(self):
        cmd = self.cmdToBytes(commandHeaderAndAddress+"002232")
        self.writeToHuskyLens(cmd)
        return self.processReturnData()
 
    def command_request_learned(self):
        cmd = self.cmdToBytes(commandHeaderAndAddress+"002333")
        self.writeToHuskyLens(cmd)
        return self.processReturnData()
 
    def command_request_blocks_learned(self):
        cmd = self.cmdToBytes(commandHeaderAndAddress+"002434")
        self.writeToHuskyLens(cmd)
        return self.processReturnData()
 
    def command_request_arrows_learned(self):
        cmd = self.cmdToBytes(commandHeaderAndAddress+"002535")
        self.writeToHuskyLens(cmd)
        return self.processReturnData()
 
    def line_tracking_mode(self):
        cmd = self.cmdToBytes(commandHeaderAndAddress+"022d030042")
        self.writeToHuskyLens(cmd)
        return self.processReturnData()
 
    def face_recognition_mode(self):
        cmd = self.cmdToBytes(commandHeaderAndAddress+"022d00003f")
        self.writeToHuskyLens(cmd)
        return self.processReturnData()
 
    def object_tracking_mode(self):
        cmd = self.cmdToBytes(commandHeaderAndAddress+"022d010040")
        self.writeToHuskyLens(cmd)
        return self.processReturnData()
 
    def object_recognition_mode(self):
        cmd = self.cmdToBytes(commandHeaderAndAddress+"022d020041")
        self.writeToHuskyLens(cmd)
        return self.processReturnData()
 
    def color_recognition_mode(self):
        cmd = self.cmdToBytes(commandHeaderAndAddress+"022d040043")
        self.writeToHuskyLens(cmd)
        return self.processReturnData()
 
    def tag_recognition_mode(self):
        cmd = self.cmdToBytes(commandHeaderAndAddress+"022d050044")
        self.writeToHuskyLens(cmd)
        return self.processReturnData()
 
    def command_request_by_id(self, idVal):
        idVal = "{:04x}".format(idVal)
        idVal = idVal[2:]+idVal[0:2]
        cmd = commandHeaderAndAddress+"0226"+idVal
        cmd += self.calculateChecksum(cmd)
        cmd = self.cmdToBytes(cmd)
        self.writeToHuskyLens(cmd)
        return self.processReturnData()
 
    def command_request_blocks_by_id(self, idVal):
        idVal = "{:04x}".format(idVal)
        idVal = idVal[2:]+idVal[0:2]
        cmd = commandHeaderAndAddress+"0227"+idVal
        cmd += self.calculateChecksum(cmd)
        cmd = self.cmdToBytes(cmd)
        self.writeToHuskyLens(cmd)
        return self.processReturnData()
 
    def command_request_arrows_by_id(self, idVal):
        idVal = "{:04x}".format(idVal)
        idVal = idVal[2:]+idVal[0:2]
        cmd = commandHeaderAndAddress+"0228"+idVal
        cmd += self.calculateChecksum(cmd)
        cmd = self.cmdToBytes(cmd)
        self.writeToHuskyLens(cmd)
        return self.processReturnData()
 
    def command_request_algorthim(self, alg):
        if alg in algorthimsByteID:
            cmd = commandHeaderAndAddress+"022d"+algorthimsByteID[alg]
            cmd += self.calculateChecksum(cmd)
            cmd = self.cmdToBytes(cmd)
            self.writeToHuskyLens(cmd)
            return self.processReturnData()
        else:
            print("INCORRECT ALGORITHIM NAME")
            


Schematics

project-image
Schematic - 76.1


project-image
Schematic - 76.2

Downloads

3D_printer_movement_and_status_tracker_case_v1.stl

Download


3D_printer_movement_and_status_tracker_cover_v1.stl

Download


telegram_3D_printer_bot.zip

Download


lib.zip

Download


Fritzing

Download