OffSetLab
offset \ˈȯf-ˌset\ noun
a force or influence that makes an opposing force ineffective or less effective
Cuply - Hydroponics Greenhouse Cabinet Powered by Django Channels and Arduino - Front-end
I'm in the process of making a greenhouse out of a glass cabinet that sits on our balcony. Inside it, there's to be a plethora of sensors and actuators hooked up to Arduino Due microcontroller, which is in turn connected to an old Raspberry Pi. The software stack is Django with Channels in the back-end, and React with some D3 in the front-end. It was a project long time in development, and this series of articles will explore it from the inception to the latest product.
Front-end ← You are here
This summer I set out to work on Cuply so I containerized the thing and set up the Raspberry Pi to house the project. I destroyed one SD card in the process but it's still working with the old one. Just a bit too cramped for my liking. When setting up the hardware I did some soldering (for which I bought the soldering iron) for the relay contraption. I tested out the pumps, checked for leaks and sealed them with silicone glue. For the back-end part I wrote majority of tests and implemented the coverage for it. I still need to push the repo to the cloud. Stay put for that one.
It was time for me to move to the front-end, which this article is about. The progress has been slow, but the Cuply project is moving forward despite outside distractions. The front-end architecture is driven by React. The dashboard is using the Bootstrap CSS framework and there are some D3 graphs that display the state of sensors. D3 bits are heavily modified examples taken from Observable.
I don't have a user registration system in place at the moment so when setting the project up, a user needs to be created in the back-end with createsuperuser Django management command. It's not an issue to have a superuser here because the project is isolated in the local network and the user is the only one existing in the project. Parameters can also be changed with the enabled Django admin.
A microcontroller is also needed to be set up in the Django admin, but at this point I'm thinking about hardcoding it in the settings and getting rid of the database interaction since there's only one microcontroller present. To ease my trouble, I exported the fixtures of the user, profile, microcontroller and devices so I can recreate them at will with loaddata management command.
Containerizing it spins up two containers. One for queue for the websockets and the other one for the app itself. The latter exposes the Arduino from the host so the order of things when spinning it up matters. Plug in the Arduino in the electrical outlet, then plug it in the Raspberry. This is important because the Arduino requires its own power supply, otherwise it makes Raspberry unstable.
Front-end bit is completely isolated and communicates with the back-end via REST API, websockets and authorizes itself through JWT. It is built with create-react-app .
When turning on the interface in the browser for the first time, a modal with logging in is presented to the user and one user/password combination later, the rest of the interface is unlocked. The navigation consists of the standard buttons for the main page and for signing out, then the items that are:
The first three display and edit the state of the items in the cupboard. Trends deals with displaying the graph of the historical data of the sensors and Settings changes the user's API key for FloraCodex. Editing the specific item opens a modal with a form to be changed and submitted back on the server via REST API. The real time data is fed to the dashboard through the websocket.
Here are some screenshots:
Cuply - Hydroponics Greenhouse Cabinet Powered by Django Channels and Arduino - Back-end
Back-end ← You are here
Back-end architecture is driven by Python and Django. I am skilled with that stack so I wanted to make something with things I know how to work with. Because of the smaller footprint that the Raspberry Pi has, I had to reduce the number of active processes that would potentially consume the memory. What I ended up with is:
Redis server that serves as the support for the websockets
Django ASGI process that would deal with both, sockets and web traffic, but another thread is spawned to take care of the sensors and actuators, as explained further down
Docker daemon composing the application
The database is SQLite since it's only one user per cabinet. No need to have Postgres there. Originally I was thinking to go raw with Circus managing the processes (I wouldn't go with Supervisor because Circus is simpler and handles sockets as well), but this proved to be better encapsulated with Docker so I wrapped things in it instead.
When an ASGI process is created (because it has to handle websockets), an app gets initialized and there's a piece of code in apps.py that spawns another thread. I had to do it this way since that other thread houses an infinite loop that would block the execution of the normal server code. In that new thread, a loop manager is created and executed, which in turn has several functions called in an infinite loop. They are:
updating the devices needed to be parsed if the device list is changed in any way
updating the readings from the sensors
running actuators depending on the readings from the sensors
communicating the current state to the websocket
saving the snapshot of the state for trend graphs
I had to have the Arduino blueprint sketch written in a C variant, but since I don't know C that well, what it does is sends the state to the USB and reacts to the input coming from the USB. This is done for the analog and digital sensors and actuators, for I2C and for communicating with the servo if attached. The idea is that devices in the cabinet are:
analog light sensor
analog ambient humidity sensor
I2C water level sensor (a capacitive one to prevent the corrosion)
servo motor for pushing and pulling the door of the cabinet through the rack and pinion mechanism
three digital actuator relays for controlling the submerged pump, the air pump and the LED strip
The meat of the code is in Python that wraps the pySerial library around Arduino microcontroller itself. The wrapper methods are in turn called from two places. Django models and the finite state machine library called automat from Glyph. Communicating the state is sending the JSON message to the channel layer for the front-end to parse.
The other bits of the back-end application is a common auth system used by Django together with REST framework to serialize the messages for interfacing with FloraCodex via my Shamrock library. Originally Shamrock was interfacing with Trefle, but Trefle is now discontinued and FloraCodex is picking up the pieces. It works OK for my use-case. At least in the initial test. This might warrant another article, but I'll probably mention it in the future eventually.
Cuply - Hydroponics Greenhouse Cabinet Powered by Django Channels and Arduino - Hardware
Hardware ← You are here
The hardware side came to be an IKEA glass cabinet. We opted to buy one and be done with it. I assembled it indoors and we took it out to the balcony. It fit the space we had in mind for the project and we felt it was good enough. The glass walls made it a greenhouse and, because it resembled a cupboard, the name was born: Cuply.
We replaced the original shelving with a suspension contraption to hold the plants. Some parts were bought in the nearby hardware store, some in IKEA, some online, some in the nearby general store, but let's list them all:
Ikea, a small version of MILSBO line, to hold everything. It's transparent on all the sides except the bottom, which is to achieve the greenhouse effect and keep things cozy.
Suspension rod
Ikea, FINTORP line rod, attached with screws from the local hardware store. We drilled into the inside of the cabinet, on the top side, to attach it. It's meant to hold the suspended plant pillars from the accompanying hooks. I also had to shorten it with a hacksaw.
Two chains per pillar
From the local hardware store, four chains in total. They measure ~50cm in length. The links have to fit the hooks on the suspension rod and the turnbuckles.
Three turnbuckles per pillar
From the local hardware store, six in total. Each turnbuckle goes through a bottle so it holds it in place.
Six plastic bottles
They're rather small, 1L actually, but one has to start somewhere and we drank a lot of juice to get them. Bottom half is sprayed white to reflect the sunlight and shield the sensitive roots. Black electric wire isolating tape is added in the middle just for the looks, to hide the transition from white to transparent. The front of each bottle is cut out to allow the plant to stick out. Bottle sides are drilled on the left and right for the turnbuckle to go through. A plant pillar consists of three bottles, turned upside-down and connected by plugging the bottle cap of the previous bottle into the bottom of the next one, which requires drilling a 2.5cm hole in each bottom to push the cap through. Each bottle cap and the bottom of the first bottle have a 5mm holes drilled into them, to connect to the water system loop and circulate water through the pillars. Each bottle houses a hydroponic pot (it's like a regular pot, but like a net) with clay pellets. There are two pillars, three bottles per pillar - six bottles in total.
Upper tank made from a soil drain pipe
10cm diameter, 75cm in length. Both ends are covered with a rubber cap bought online. There is a hole at the top, 2.5cm in diameter, for the water inlet, and two small holes at the bottom of the tank with aquarium drip valves to control the flow. A small piece of aquarium tubing leads from each bottom hole into the top hole of its respective plant pillar. The drip valves are sealed with silicone (which is safe for aquatic life) so they don't leak at the sides. The upper tank is suspended above the pillars and held in place by two pipe clamps and some screws from the local hardware store.
Lower tank also made from a soil drain pipe
10cm diameter, 75cm in length. Both ends are covered with a rubber cap bought online. There is a hole at the top, 2.5cm in diameter, for the water outlet. A small water pump is submerged in the lower tank with a power cable going out the cable hole on the side. The outlet of the lower tank is connected to the inlet hole in the upper tank via the provided water pump tube (via nozzle), approximately 13mm in diameter and of ~150cm length. The pump outputs 1500L of liquid per hour so it's fast and strong enough. Another hole on the top, also 2.5cm in diameter, is drilled on the other side to replenish the water and to provide the access to the air stone from the air pump to keep the algae from forming in the tank. The cap of the bottom bottle of each pillar has a small hole drilled into it, 5mm in diameter, from which an aquarium tube feeds back into the lower thank, allowing it to be filled up again. There's also another ~2mm * ~20mm slit on the top side for the I2C capacitive water level sensor to go into the bottom tank. Could be that more sensors can be added, but this is my experimental limit for now. It is also held in place by two pipe clamps and some screws from the local hardware store.
Light, temperature, ambiental humidity sensors
All analogue placed inside the cabinet.
For controlling both of the pumps and the light. Three in total.
Arduino Due
All the sensors are attached to it and it has its own power source since all of it cannot be powered through the USB. Arduino Due is used because it supports a wider plethora of sensors than GPIO of Raspberry PI.
A first generation model B+ is put to use here. It has a WiFi antenna in one USB slot (because it didn't come with an integrated WiFi interface) and Arduino Due is plugged in the other USB slot to report the state of the sensors (and act on the actuators).
LED strip in the cabinet
Held in place above the pillars to provide additional lighting and connected via relay to the Arduino Due.
Everything is neatly connected in one plastic box and fed power coming into the cabinet from the nearby outlet.
The idea came from the Window Farms kickstarter so kudos to those folks. Even though the project is discontinued, it gave me inspiration for the hydroponic loop. The plants are nested in each bottle and are of a herb type, compatible both with the ambiental conditions as well as with each other. There are various attempts online at controlling similar systems via computers. Mine is no different. The systems are known as Grow Boxes and there are most likely communities around them.
The lower tank has an inlet for the solution that is made from water and a fertilizer concentrate bought in store. It needs to be changed weekly or when it evaporates. Once things are set up, the system runs the loop by pumping the solution from the lower tank controlled by the relay and turned on by the sensor, then letting the gravity take care of the rest by dripping the solution from the upper tank all the way through the pillars to the lower tank. The whole cycle is repeated ad nauseam. The lights turn on in the low light conditions.
Here are some photos from hammering on the hardware:
Cuply - Hydroponics Greenhouse Cabinet Powered by Django Channels and Arduino - Overview
Overview ← You are here
Since we started looking to buy an apartment back in 2018, we had in mind a south-facing balcony. In the end, we were lucky enough to buy just such an apartment and, even though the balcony is small, we got it to become a special corner of our living space. It doesn't have much, but you can sit there and enjoy the view of the treeline across, day and evening, have a drink or even a meal on the foldable table we installed. The previous owners had a water butt set up to pick up the drain water and a set of flowering plants that thrived on it. We wanted herbs we could use in the kitchen so we had to control the water quality. Because the balcony is small, we opted to try a vertical set-up instead of a horizontal one, and having a hydroponic loop made sense. Fast forward to 2022. We have an automated system, although that's still just the beginning.
Even though sunlight is abundant on south-facing balconies in the northern hemisphere, if there are no outside obstructions, Ireland is not ideal. To control the temperature better, we opted for a glass greenhouse to look nice and make use of the greenhouse effect. For that, we had to buy a glass cabinet because building one was hardly an option. No space for us to do it at home, and the hackerspace we know is not really near enough to reach conveniently without our own transport.
Whoever opts to build such a thing by themselves, by all means, go for it. Custom builds will always utilize the available space better.
We could've become members of a nearby community garden. It's looking lovely this year. But we'd definitely be too lazy to devote ourselves to maintaining it so the balcony greenhouse will have to do. And, to be fair, setting up hydroponics is more fun.
Of course, there are some drawbacks. The space is limited in general and we'll see how viable the plants will be (the pots are rather small), but it's an experiment driven project that is inconspicuous enough, but also decorative and useful; it enriches our balcony's environment, creates a separation from the neighbours and might yield fragrant herbs. Not to mention that it's a great opportunity to have a hobby.
In the future articles, I'll go into more detail on how the thing was made. For now, it's a hydroponic loop that functions between two tanks. The lower one pumps the water into the upper one, and the gravity takes care of the rest. Because of the size, only six plants are able to be inside the cabinet. The other things are nifty add-ons that just make the whole thing a bit high-tech and agro-informatics driven. Whatever the case, I'll open-source it after I finish the articles and have a working cabinet, so whoever wants to explore a bit and implement a similar solution in their place, they're more than welcome to use it.
3D Printer Shenanigans
Buying a 3D printer was a decision we postponed for a very long time. With Vesna delving deeper into DM-ing D&D, we were thinking that we could host some games IRL for friends. Heck, I could even make some cocktails because we got a cheap cocktail making kit. Having miniatures would be nice, printing them would save money. Having a 3D printer would be useful for other models like small hardware parts, gifts, fixing broken things, etc. In the end we decided to bite the bullet and opted for a high resolution resin printer because they have gotten affordable enough and the level of detail is amazing. Right now we have Phrozen Sonic Mini 8K resin 3D printer.
What surprised me the most was that people who tinker on their computers usually have a notion that 3D printing is still filament only. Having a spool of plastic thread melted and drawn with, layer by layer, is not the only way. The other type is resin based, where the build plate is submerged in a vat of photopolymer liquid resin. The resin gets exposed to UV light from the screen below the vat and hardens on the build plate, layer by layer. The model is pulled out of the vat upside-down. These printers are faster by design because they harden the entire layer at once instead of a single dot drawing. They also have a higher resolution and less visible ridges between layers, but are messier and one needs to deal with toxic chemicals. To understand resin a bit more, Brent, from Goobertown Hobbies, explains the resin from the chemist perspective better than I would ever do.
There's also a concern where the fumes from melted plastic are a health hazard that needs to be addressed, but also the evaporation from resin, which needs more study. That's why 3D printers should be kept in a well ventilated room and PPE should be used. The toxicity for the environment and recycling the waste are also a concern. We don't plan to print a ton of miniatures, but we still need to address the waste properly. Like any other dangerous chemical, resin needs to be treated with respect. UV curing the liquid form renders it inert because monomers eventually form polymer chains that are not as harmful to the environment. The outcome is the same, the waste needs to be properly disposed of.
We bought Artisan Guild Patreon subscription to get some really nice miniatures for D&D. Surrounding a campfire with some heroes is a good start. We aligned all the models we wanted that could fit the printer margins. This was done in Chitubox slicer tool and the presupported files had most of the planes under 45 degrees vertically to counter the effects of gravity. The presupported files are great, but Chitubox can do it automatically if your models don't come presupported. One needs to know why printing is done at an angle and align their models properly so the need for the supports is reduced and it's easier to print without ending up with bent model planes.
Chitubox also needs a profile for the machine so it knows the parameters like screen resolution for the printing and what kind of resin is used. Regarding the printer itself, the profile file can be picked up from the manufacturer's pages and loaded into Chitubox.
As for the resin parameters, we needed to consult resin documentation. The problem was that the parameters were provided on the assumption that we would be using the same manufacturer's printer as well. We had to dig a bit to find the correct parameters for our use case. Exposure times and lifting speed are paramount for a successful print. After they were set, we sliced the file and had it ready for printing.
The resulting sliced file was under 250 MiB with over 1000 layers and around 50 grams of resin would be used. The bottle is 1 KG so it wasn't a lot. With the correct settings (don't ask, we had to print twice and we found out about the proper instructions afterwards), the printing process would take around seven hours.
There are a few steps we also had to do:
Upgrade the firmware. Phrozen has instructions for their printers so the resulting files from recent Chitubox versions can be compatible;
Test the LCD. There's a button for it in the printer. Inspect the screen for any deformities;
Calibrate the build plate. Also a button that gives out instructions. Align the build plate accordingly.
With the above done, and setting up the workstation and cleaning equipment (we used pet food trays to work on), it was time to print. PPE was used all the time: safety glasses, breathing masks, nitrile gloves. We also bought Elegoo carbon filters and put them in the printer chamber and turned them on. I know the melted plastic smell because I visited the plastic factory way too often when I was a kid. This one is not that bad, but still we'd like to play it safe. We then shook the bottle of resin for a minute and then opened it and poured the contents out in the resin vat. We covered the printer, inserted the USB memory stick with the file from Chitubox and kicked off the printing. Our window was also open so fresh air could circulate in the room with the printer.
When the printing finished, the resulting models were still dripping a bit, so we left them for a couple of minutes for the majority of liquid to drip away. It was time to wash and clean. With PPE back on (safety first), we removed the printer cover and transferred the build plate, with the printed models still attached to it, into the washing tub of our washing and curing station from Anycubic. We then filled the tub with tap water and covered it. We used the resin from Elegoo that is water washable because IPA (isopropyl alcohol) is tricky to handle and expensive to buy. That said, please have some. We bought a bottle in the local pharmacy because it is invaluable when cleaning the equipment after printing.
We covered the printer again with the lid to prevent the smell and the stray UV rays from reaching the resin in the vat and started a ten minute washing cycle in our washing/curing station. It has a lab type mixer so it was creating a vortex in the tub. After it was done, we removed the tub and pulled out the build plate from it.
It was time to remove the models with a spatula. They were really stuck. Safety equipment is especially necessary here because the crumbling resin supports start flying everywhere. After the models were dislodged, we immediately removed the rest of the supports by (gloved) hand and put the models on a paper towel.
We transferred the water from the tub into a separate container and left it to be cured by the sun. It must not just be poured down the drain because in uncured state it's still toxic to aquatic life. We used IPA to clean the tub, the grate, the build plate and left it all to dry. We then put the washing/curing station in the curing mode and put the models in it to cure for another two minutes. This is the general time for curing and it should dry them out. They were done, but we weren't.
We uncovered the printer once more, turned off the carbon filters and took out the resin vat. We sieved the resin back through a funnel into the original resin bottle. Some people leave it in the vat, but we cannot afford that luxury because we don't have a dedicated workshop. We cleaned the vat with IPA soaked tissues since they're softer than paper towels and have a reduced risk of damaging the FEP film on the bottom of the vat. The vat needs to be cleaned after a failed print as well. FEP film on the bottom of the vat will wear out over time so it needs to be replaced eventually.
In the end, we left the used tissues and paper towels from cleaning the equipment (like spatula), the nitrile gloves from handling it all, etc., in a transparent bin bag in the sun. The dirty waste has to be left out a few days so the UV rays from the sun cure whatever is left of the resin particles.
The washing water could be reused for the next printing sessions until there's sludge in it collecting on the bottom. At that point, the sludge can be poured onto a plate so the sun evaporates the water and cures the rest of the resin so it hardens. The hardened resin is ready for waste disposal at that time.
As for the models... they had some post-processing left like filing and polishing where needed. I have to say that we're pretty satisfied with the overall outcome. What can be done with them later is painting them, but I'll leave it for another article. Of course, if something else needs printing, other than the miniatures, I'll be sure to mention it.
A Trefle API Library for Plant Data
Convert a JSON export from a Trello board to .mm file for Coggle.
Krita-Leaflet
A Krita plugin for tiling and slicing images for Leaflet.
Geotag Gallery
Make KML file for loading in Google Earth from folder of geotagged photos.
Reddit Favorites Coloring
Colorize favorite Reddit subs on homepage.
Gimp-Leaflet
A GIMP plugin for tiling and slicing images for Leaflet.
Solar Projector
Real-time tracking of solar wind velocity, temperature and proton density. RETIRED.
Last of the Season
The News of Our Death Have Been Greatly Exaggerated
Drawing Setup
Cuply - Hydroponics Greenhouse Cabinet Powered by Django Channels and Arduino - Conclusion
OffSetLab serves as a platform for life, tech and hobby related rumination and exploration, as well as a showcase of ongoing projects in both web development (mostly reliant on Python and JS as understructure) and drawing (mostly reliant on persistance against common sense). The common threads that run through all these disparate efforts are the pursuit of work-life balance, environmental responsibility and technology as a public good.
OffSetLab All rights reserved.