We were wondering about how to bring a custom camera solution that would be affordable and easy to integrate into the more than 400,000 original Prusa printers we have already shipped worldwide. While we have other solutions in the works, we would like to highlight a great open-source solution that was made specifically for Prusa Connect by a member of our community, Miroslav Pivovarský – an ESP32-CAM module with custom firmware.


This affordable module has integrated flash LED and WiFi, and you can buy it pretty much anywhere, flash it with Miroslav’s firmware, and start using it right away. It is not particularly revolutionary, but it provides an easy, cheap, and most importantly, functional solution for remote camera monitoring of your printer.

Once you flash the firmware, the entire setup is a matter of a few clicks. After that, all you have to do is register the camera in Prusa Connect, copy over its access token, and in a few seconds you will see its first snapshot in your interface. Do not expect a 4K60 video stream though, with the current implementation, it sends an image every couple of seconds, but even that is more than enough to remotely check on your print. Or even to send the image into a neural network for automatic fail detection, as we teased in the Prusa Podcast episode about AI in 3D printing. And since the firmware features OTA updates, the functionality can be improved in the future.

Detailed instructions for the entire setup can be found in this PDF. You can also print a handy camera mount that can be attached to the frame of your 3D printer. It’s flexible, yet sturdy, and you can easily set the position of the camera on it. It was created by the great designer, Michal Fanta and you can download it for free from Printables, along with the case for the camera module.

And if you’re wondering what it takes to develop such a solution, including the web application interface, OTA firmware updates, and many other interesting things, let us give you the detailed story behind its development! 🙂

The Story Begins

Miroslav began to explain how the work on his camera solution started: “We had a couple of MK3S+ 3D printers at work, and they were connected to the network via a Raspberry Pi with running PrusaLink software.” PrusaLink lets you get advanced network functionality even on older 8-bit machines and it allows you to connect them to Prusa Connect. “I was using an older smartphone as a remote camera (a built-in feature in Prusa Connect), but I was also looking for something more compact. Ideally, a small camera that would be functional just by itself,” he added.

So, in the meantime, he began to analyze the inner workings of the system – particularly how the phone sends the image to Connect. It didn’t take long to modify the NodeJS script that manages image transfers, so it could obtain captured photos from a small Raspberry Pi Camera via the CSI bus.

Miroslav then uploaded the script to another Raspberry Pi, connected the camera to it, and executed the script to grab the picture from the camera and send it to Connect. However, there still was a downside: it required two RPi boards – one for managing the camera, the other for running PrusaLink. It worked, but as Miroslav said, it was far from ideal.

Camera attached to Original Prusa MK4

Camera attached to the Original Prusa MK4

As it often happens, a breakthrough came by sheer coincidence. “I was just cleaning my desk and drawers the other day because I have plenty of various Arduino modules and addons – and there it was! A small ESP32-CAM module.” Miroslav remembers. This cheap MCU (Micro Controller Unit) has its own ARM processor, large Flash memory, enough RAM, and also a Wi-Fi module. It was perfect! 🙂

Of course, the path to a working solution wasn’t completely straightforward. While it might have seemed that the basic Arduino IDE (the development environment) would be enough, it wasn’t exactly true. In the end, Miroslav created his own HTTP server to receive and save incoming images, just like Prusa Connect. With this server, he tested the first versions of his scripts – those that were already working on the Raspberry Pi.

It works on my Raspberry Pi!

The next step was pretty obvious – make the ESP32-CAM do the same job the Raspberry Pi does. It was necessary to understand how the camera module works and how to send photos not just over HTTP, but also via HTTPS, which Prusa Connect uses, and which is more complex to work with. However, after some research, obtaining and correctly setting up the necessary certificate, Miroslav successfully sent the correct data from the ESP32-CAM to the server and even to Prusa Connect! Everything seemed to be working just fine, but the first problems appeared right away.

One of the biggest issues was with the size and resolution of the sent image. The maximal resolution, that was sent without any problems, was 320×240 pixels, which isn’t particularly much. This limitation was caused by the ESP32-CAM module itself, in which the problem occurred, when the size of the file, sent via HTTPS was larger than 9kB. After some research and several attempts, Miroslav found out, that if he split the file into the 5kB fragments and sent them sequentially, everything works correctly. Although it would cause slightly slower uploads, it was an ideal solution, at least for the moment.

It was even enough to create a video stream for a local network. The original concern was the maximum framerate would be around 1FPS, but after some problem-solving, where the streaming caused various color artifacts and poor horizontal sync, the results showed 5-7FPS at a resolution of 1024×768, which was a great result 🙂. And the MCU should have the potential to get even more FPS and better resolution, so Miroslav keeps working on it.

Now, Miroslav had a simple, but functional software solution, which he immediately shared on GitHub and the Prusa forum, because he just wanted to be the first, who came up with this solution.

Prusa Connect dashboard

You can view the latest image from the camera in the Prusa Connect application

There were still many things to solve, for example, it wasn’t ideal that the whole MCU configuration is integrated into the source code – the token, fingerprint, WiFi SSID, camera setup, and more. This whole package is compiled and flashed into the MCU memory, without the possibility of any additional changes. So, Miroslav began to look for a solution in a field with which he had little experience – the web interface.

Although web development was not Miroslav’s main focus, he took to his idea with enthusiasm and after several evenings of programming he came up with a functional web application for camera configuration, which ran directly on the camera module. “It looked like it came straight from the 90s, but it worked, so I was okay with that.” Miroslav laughed.

The first version of the camera web interface

One of the first versions of the camera web interface

On the rails of development

We also noticed his solution and liked it very much, so we contacted him via email and offered him cooperation with the development of it. “I was very pleasantly surprised by the offer and immediately agreed to it. Among other things, because I would like to contribute more to the open-source community,” Miroslav said.

After some discussion and code review, we came up with several ideas for improving the software and web interface, for example, the implementation of a more modern-looking user interface, etc. Miroslav took up all these points with enthusiasm and started working on them immediately. “The most challenging part of the whole project for me was the development of the web application itself,” said Miroslav. The website had to look good and modern, ideally in a Prusa design, while still fitting into the MCU’s limited Flash memory. On the other hand, after analyzing and reading several articles, he evaluated, that his current solution, involving a combination of HTML and JavaScript with jQuery is the right way to go.

Miroslav also took the opportunity to work with AI, which helped him in the creation of the site. For example, he advised and explained the individual components, what they are called, how to use them, etc. “It was funny. I tried to explain to the AI what I wanted, but I didn’t even know exactly what I wanted. I just knew what I wanted it to look like in the end,” Miroslav mentioned. He also admitted that even though the web interface could have been more optimal and nicer, he had a great time developing it and learned a lot of new things.

The need for configuration

Now, when the design of the web interface was pretty much finished, he started thinking about other features. For example, firmware updates should be possible via the web interface – the camera could check the latest firmware version and download it if needed, and everything should be as user-friendly as it could.

Current camera web interface

Current appearance of the camera web interface

Now it was time to deal with one of the very important things, which was the first launch and setup of the MCU. Miroslav came up with two different solutions, that can exist next to each other and everybody can choose the preferred way of camera initialization.

The first solution is the AP mode, where an access point with a specific SSID is created after switching on the MCU. The client can connect to the AP via WiFi and here he can set the login details of his wireless network and further configure the camera. To prevent unused APs from unnecessarily interfering with the 2.4GHz WiFi band (imagine having dozens of them turned on), if no device connects within five minutes of turning on the camera, the AP mode is automatically turned off.

Once your camera is set up, you can locate its administration in the network via the IP address, but this is not very convenient, especially if you don’t know the IP address and don’t have access to your router’s administration. To ease this, Miroslav also added the mDNS functionality to the camera software, which means you are able to access the device in your local network via a DNS record (you can think of it as a nickname), so you don’t need to know or remember its IP address. The mDNS record is configurable – for example, now it is http://prusa-esp32cam.local

However, the AP mode solution brought up a problem, because the web interface needs to work in offline mode, while the user is connecting to the camera in the AP mode. Because of this, it was necessary to store the jQuery library directly in the MCU’s source code. Fortunately, small and optimized jQuery libraries already exist for this purpose, although it still takes approximately 86kB of MCU’s Flash memory, and that’s quite a lot.

The camera SSID consists of the prefix ESP32_camera + ID, which are the first three numbers from the UID of the MCU. Therefore, if someone uses several modules – for example for monitoring the farm – with the help of this label, they can easily recognize which is which.

Another way to configure the camera was a serial communication interface, that users can use instead of AP mode if they can’t or don’t want to use it. Here, the user can set the WiFi credentials and the authorization token for the backend application, display the IP address of the camera in the network, or use other commands via the console.

The bigger it gets…

At this point, the project has grown quite a bit. Miroslav rewrote the website about 3-4 times because he always found something that could be done better. The first version of the code was written in C, so now he tried to rewrite everything in C++, which is still not completely done, but the code is being edited and improved part by part. Although the project has grown quite a bit, it started and continues as an Arduino project, so the community can easily edit the source code without having to use another dedicated IDE with a compiler.

Now it was time to figure out how to implement the OTA (over-the-air) firmware update. The current MCU version has 4MB of Flash memory, which means that only 1.9MB of Flash can be used for the whole application. If you wonder why, it’s because for the firmware update, the so-called “double banking” is used, which means that the memory is divided into two sectors (it’s actually more, but for the sake of simplicity, let’s say two). In one sector, there runs an up-to-date, valid application (we’ll call it sector A). In the other sector, there is an invalid, outdated application version (you guessed it, it’s sector B).

Camera system information with OTA

Camera system information, including OTA firmware update options

When the new firmware is downloaded, sector B (the invalid application) is overwritten. Then the integrity of the installed firmware is checked, and if everything is OK, the application in sector B is set as the active one. Thanks to this system, an OTA update never overwrites the currently running application.

Hardware limitations

In addition to various software problems, there were also a few problems with the hardware itself. Some of the test samples stopped sending photos, kept disconnecting from WiFi, rebooted, etc. “It was many different problems, but I was having fun solving them. It was very interesting to see how they are occurring and then come up with solutions to eliminate them,” Miroslav commented.

The biggest problem was caused by the design of the ESP32-CAM board. For example – since the processor has few pins and a relatively large number of peripherals, the designer used the pin for lightning the LED also as one of the pins for communication with the microSD card. This resulted in a problem with communication between the MCU and the microSD card when the LED was lit. This was solved by using the microSD card’s “one wire mode”, where data is read and written only through one digital input/output.

The much bigger problem is with the flash LED on the board – the designer connected this LED directly to 3.3V without a current-limiting resistor, which causes the current flowing through the LED to be too high and the diode will die after a certain time. On one board the LED went out after a month of flashing every 30 seconds. Miroslav came up with several solutions to this problem, including soldering a resistor between the collector of the transistor and the PCB, or using an external light source, switched by a relay, a MOSFET, or something similar. Some pieces of the board also heat pretty much, but it depends on the current batch. This issue is still being worked on, because several attempts like reducing the clock frequency of the processor were tried, but they did not bring much improvement.

Possible solutions to the LED problem

The few possible solutions for the LED problem suggested by Miroslav

As you can see, there are still many things to solve and improve in this project. However, Miroslav still enjoys it and likes to work on it. What he enjoys most is finding out what can be done with a small, cheap camera module. He would like to work even more on multitasking, since everything runs simultaneously on only one processor core, although the processor has two cores (dual core). And there’s sure to be a bunch of other tweaks/improvements he’d like to add.

So, do not hesitate to try this interesting camera solution, and also please share any of your comments or questions with us and the author.

Happy printing!