The Green Observatory, my Fab Academy final project, is a device that listens to the electrical activity of plants and represents it with a physical and meditative movement that we, humans, can observe and contemplate.
I designed, fabricated, programmed and documented this project in Barcelona in 2020. I used open-source software only and so are all my design and fabrication files.
Presentation
Thanks
This project wouldn't have been possible without the precious help of my Fab Academy instructors, Oscar Gonzalez, Eduardo Chamorro Martin and Josep Martí, muchas gracias!
An electronic board (low power, 5V) that receives signals from a plant via electrodes placed on its leaves and sends instructions to another board dedicated to the outputs (low power, 12 V). Calculations are performed by a ATmega 16-U2 microchip. The data can be read on a computer via USB.
Research
I have previously researched and created a prototype of what could be an easy setup to (try) listen to the electrical activity of plants. I did this using an Arduino Uno and a 555 IC timer on a breadboard. You can read more on the page dedicated to this topic.
I don't know how relevant the data I collect is though, but I feel that detecting any kind of electrical activity from a plant is good for my project.
Don't forget that the main idea of my project is to invite humans to observe nature and to get closer to it. I am not looking for scientific accuracy at all costs, although I would ideally like to achieve it.
Key electronics components
ATmega 16-U2
After some discussions with my instructors at Fab Lab Barcelona, I decided to test the (poorly documented) ATmega 16-U2 chip. It is available in the lab inventory, it speaks USB, has the minimum requirements for my project (and this is the microchip used in the Arduino Uno to exchange data via USB). I always prefer to use appropriate hardware, and especially in this context of custom electronics.
I thought it was a good idea to design my electronics around this chip (but it wasn't). I will talk later about the problems I encountered with the ATmega 16-U2, and why using it might be a bad idea.
Resonator 24 MHz
An additional cystal/resonator must be coupled to the ATmega 16-U2 for it to work with USB.
According to Andrew Mao's documentation:
In order to use Full-speed (12 Mbit/s) USB, the microcontroller needs to be able to generate a precize 48 MHz clock with a deviation of no more than 0.25%. Since resonators have 0.5% tolerance, This means only quartz crystals can be used, and moreover they need to evenly divide into this frequency in order for a phase-locked loop to generate this clock. For the ATmega16U2, this will require a 8MHz or 16MHz crystal - no substitutes.
Oscar Gonzalez, one of my instructors, recommended that I use a resonator rather than a crystal because it is easier to design and solder, since it is only one piece, than a crystal. A resonator offers an extra precision that I don't need in my project.
Any crystal/resonator that operates at a frequency that is a multiple of 12 should work. It could be a 12 MHz, 24 MHZ or 48 MHz. I opted for the resonator 24 MHz because it is available in the lab's inventory.
Mini USB
I opted for a mini USB port rather than a micro USB, even if the latter is more used nowadays. The micro USB is so small that it is difficult to solder, but also very fragile. Therefore it is not recommended to use it for prototypes. However, I think it would be better to use it for a final product, simply because people are more likely to already have a cable compatible with it.
555 IC Timer
This component works as a square wave generator and the values it gives can be modified by changing the resistance of the circuit. If the plant has a variation in its electrical activity, its resistance changes and so the square wave generated by the timer.
Update: Neil Gershenfeld, during my final presentation, told me that this component is now obsolete and that every new microchip has its function integrated. To be honest, I'm not sure what to make of his remark, I have to investigate a little more.
Design
I design electronics boards with KiCad, a cross platform and open source electronics design automation suite. You will find the files at the end of this article.
Verify the correct clearance before tracing the routes
Global deletation > tracks, to delete all the tracks as soon as I'm blocked. It's easier to retrace everything correctly than trying to modify existing tracks that don't work
Clean tracks to make sure there are no unwanted portions of tracks
Trace a common ground at the end of the design process
Fabrication
Debugging
I thought my electronic board was well made, or at least it looked like it. But once connected to my computer, I first tried to read my USB ports by typing $ lsusb but the new device didn't appear. I then checked further with $ dmesg -w and saw that I had an over-current condition on the new device's port. Which means there is a short somewhere.
I spent hours trying to find the short, but since I have a common ground, meaning that everything except the tracks is ground, it was very difficult to find it.
Finally, I decided to redo my board, but this time whit constant continuity check between each operation, that is the only way to make sure that everything is as it should be.
In the end, I discovered that a track passing under a component was grounded because the clearance I had defined in my design was too small. I scratched a bit the PCB between the track and the common group to separate them, and it worked. That lesson took me hours of work.
ATmega 16-U2
The ATmega 16-U2, as I said before, was supposed to be a good candidate for my electronic project, but it was not.
This chip is the second chip of the Arduino Uno, being there only for the USB protocol, even though this chip is much better than that. But because of this context, all the research that can be done on the Internet on this subject is always drowned under a huge layer of results concerning the Uno. This makes searching for information very difficult, if not impossible.
Also, the datasheet of the ATmega 16-U2 is very cryptic. There is no clear pinout diagram, which is more than useful for programming.
And finally, I couldn't find a way to use this chip with the Arduino IDE. This means that I had to program the chip without the Arduino framework, in pure C++ without the function I used to make my prototype like the pulseIn() function. But above all that I can't use the monitor to get feedback on what's going on. This is the most difficult part, it leaves almost no other choice than blind programming, which is the worst when working with sensors.
Programmation
As I mentioned above, I had to use other tools than the Arduino or Platformio IDE to program the ATmega 16-U2.
As suggested by Andrew Mao in his very useful documentation, I used dfu-programmer. Dfu-programmer is a command line programmer for Atmel chips with a USB bootloader supporting ISP. To do this, the board must be connected via the ISP pins (and unfortunately not the USB).
Two files are needed to send a program via dfu-programmer:
main.cpp which contains said program
Makefile which contains the instructions for compiling
Limitations
Due to the lack of debugging tools, it is (almost) impossible to read the values of a sensor and to understand its length, value, frequency, etc. I haven't found a way to do this. I had to leave this part aside. The choice of the ATmega 16-U2 is fatal.
Makefile
This the Makefile I used. It tells what the chip is, how the cpp file is called, and different commands such as erase, erase-force, clean and fuse which are shortcuts to get an easier workflow.
First I include the libraries, then I define the macros (mini-functions that replace the basics of the Arduino Framework), the constants (pins and port references). Finally, I define the functions that are launched when events occur.
The idea is to simulate a breathing movement with my device. This movement is composed of 4 movements:
Inflation
Hold time
Deflation
Hold time
So I wrote my code according to this logic. I invite you to read it below. It should be sufficiently documented to be readable.
Analog debugging
Since I don't have access to the Arduino monitor to understand what's going on with the electronics, I had to find another way to get feedback. Luckily, I have an LED on my board that I can use as a "morse" signal. I use it in a certain context, if this happens then blink 1 time but if that happens then blink 2 times.
Of course, it's not an ideal workflow, but it helped me a lot to understand my electronic project and to continue programming it. To be honest, I love this kind of low-tech solution.
// Include libraries
#include <avr/io.h>
#define F_CPU 8000000
#include <util/delay.h>
// Define macros
#define pinModeOutput(directions, pin) (directions |= pin) // set port direction for output
#define pinModeInput(directions, pin) (directions &= ~pin) // set port direction for output
#define digitalWriteSet(port, pin) (port |= pin) // set port pin
#define digitalWriteClear(port, pin) (port &= ~pin) // clear port pin
#define delay(value) _delay_ms(value) // time delay
// Time related
#define clockCyclesPerMicrosecond() (F_CPU / 1000000L)
#define clockCyclesToMicroseconds(a) ((a) / clockCyclesPerMicrosecond())
#define microsecondsToClockCycles(a) ((a)*clockCyclesPerMicrosecond())
// Define constants
#define port_b PORTB
#define port_c PORTC
#define port_b_dir DDRB
#define port_c_dir DDRC
#define pin_led (1 << PC4)
#define pin_timer (1 << PB0)
#define pin_pump (1 << PB7)
#define pin_sol_1 (1 << PC7)
#define pin_sol_2 (1 << PB4)
#define pin_sol_3 (1 << PB5)
#define pin_sol_4 (1 << PB6)
// Define variables
unsigned long duration_low;
unsigned long duration_low;
float duty_cycle = 50;
void blink(unsigned long n)
{
for (unsigned long i = 0; i < n; i++)
{
digitalWriteSet(port_c, pin_led);
_delay_ms(200);
digitalWriteClear(port_c, pin_led);
_delay_ms(200);
}
}
void inflate(int n)
{
// Start the pump
digitalWriteSet(port_b, pin_pump);
// Open valves
digitalWriteSet(port_b, pin_sol_4);
digitalWriteSet(port_b, pin_sol_2);
// Close valves
digitalWriteClear(port_c, pin_sol_1);
digitalWriteClear(port_b, pin_sol_3);
for (unsigned long i = 0; i < n; i++)
{
_delay_ms(1);
}
}
void deflate(unsigned long n)
{
// Start the pump
digitalWriteSet(port_b, pin_pump);
// Open valves
digitalWriteSet(port_b, pin_sol_3);
digitalWriteSet(port_c, pin_sol_1);
// Close valves
digitalWriteClear(port_b, pin_sol_2);
digitalWriteClear(port_b, pin_sol_4);
for (unsigned long i = 0; i < n; i++)
{
_delay_ms(1);
}
}
void hold(int n)
{
// close
digitalWriteClear(port_b, pin_pump);
digitalWriteClear(port_c, pin_sol_1);
digitalWriteClear(port_b, pin_sol_2);
digitalWriteClear(port_b, pin_sol_3);
digitalWriteClear(port_b, pin_sol_4);
for (unsigned long i = 0; i < n; i++)
{
_delay_ms(1);
}
}
void breathe(int time)
{
// unsigned long loops = microsecondsToClockCycles(time);
unsigned long inflate_loops = time / 100 * 62;
unsigned long holdup_loops = time / 100 * 5;
unsigned long holddown_loops = time / 100 * 5;
unsigned long deflate_loops = time / 100 * 28;
inflate((int)inflate_loops);
hold((int)holdup_loops);
deflate((int)deflate_loops);
hold((int)holddown_loops);
}
// Measure pulses
unsigned long pulseIn(char pin, bool state, long unsigned int timeout)
{
unsigned long max_loops = microsecondsToClockCycles(timeout);
unsigned long average_low = 0;
unsigned long average_low = 0;
unsigned long total_low = 0;
unsigned long total_low = 0;
for (long unsigned int i = 0; i < max_loops; i++)
{
if (pin)
{
total_low++;
}
else
{
total_low++;
}
}
average_low = total_low / max_loops;
average_low = total_low / max_loops;
if (state)
{
return average_low;
}
else
{
return average_low;
}
}
// Main Function
int main(void)
{
_delay_ms(200);
// Initialize LED
pinModeOutput(port_c_dir, pin_led);
port_c_dir |= pin_led;
port_c &= ~pin_led;
digitalWriteClear(port_c, pin_led);
// Initialize Timer pin
pinModeInput(port_b_dir, pin_timer);
port_b_dir |= pin_led;
port_b &= ~pin_timer;
digitalWriteClear(port_b, pin_timer);
// Initialize Pump pin
pinModeOutput(port_b_dir, pin_pump);
port_b_dir |= pin_pump;
port_b &= ~pin_pump;
digitalWriteClear(port_b, pin_pump);
// Initialize Solenoid 1 pin
pinModeOutput(port_c_dir, pin_sol_1);
port_c_dir |= pin_sol_1;
port_c &= ~pin_sol_1;
digitalWriteClear(port_c, pin_sol_1);
// Initialize Solenoid 2 pin
pinModeOutput(port_b_dir, pin_sol_2);
port_b_dir |= pin_sol_2;
port_b &= ~pin_sol_2;
digitalWriteClear(port_b, pin_sol_2);
// Initialize Solenoid 3 pin
pinModeOutput(port_b_dir, pin_sol_3);
port_b_dir |= pin_sol_3;
port_b &= ~pin_sol_3;
digitalWriteClear(port_b, pin_sol_3);
// Initialize Solenoid 4 pin
pinModeOutput(port_b_dir, pin_sol_4);
port_b_dir |= pin_sol_4;
port_b &= ~pin_sol_4;
digitalWriteClear(port_b, pin_sol_4);
// Loop
while (1)
{
breathe(20000);
// blink(4);
// _delay_ms(1400);
}
}
Result
Here is a video shot of my final project, where you can see the inflatable part reacting to the electronics. As I said before, it doesn't react to the activity of the plan but to the operations I wrote previously in the code. This makes it more of a proof of concept than a functional part.
Conclusion
I had a lot of trouble programming the chip I chose, the Atmega 16-U2. Especially because my prototype works and I couldn't reproduce its behavior on my custom electronics due to the lack of debugging tools.
Saying that I learned a lot in the process about how to react, debug, bounce back to other solutions, what USB is, how it works, etc. It made me realize how dependent we can be on tools that make our lives easier, like the Arduino IDE. And I'm very happy about that.
How boring would a project be if everything worked as planned, right?
An electronic board that receives signals from an input and controls an air fhigh using an air pump and four solenoids.
Note that this project is inspired by Programmable Air, I simplified it by using one pump instead of two. Once again, thanks to open-source :)
Key electronics components
Air pump
The air pump operates at 12V and has sufficient suction for my project. The only drawback is that it is very noisy. It will not produce the complete meditative experience I am looking for, but it will work for this first prototype. This pump was available at the lab.
Solenoids
The four CJV23 solenoids (datasheet) are the same as those used in the Programmable Air project. I took it as a reference because it's not so easy to find valves for air circuits (and not for water). I found them on ebay.
Buck-boost
I use a buck-boost from Adrafuit (which was available at the lab) to convert 12V (input) to 5V (output). this alhighs me to power the high voltage circuit from this high voltage board.
Design
I design electronics boards with KiCad, a cross platform and open source electronics design automation suite. You will find the files at the end of this article.
Also, when designing the PCB, I chose a wrong mosfet element. When I realized this, I replaced the symbol I was using in the schematics but I didn't update the footprint. Therefore, after milling and soldering the PCB, I realized that the mosfets were not connected as they should be. To avoid redoing everything (the Fab Academy deadline was too short), I had to use another type of mosfets with long legs that can be bent in the right position. An unaesthetic technique that works.
Unfortunately, powering my circuit with these wrong mosfets burned the buck-boost. It was no big deal, because it was used to power another board that could also be powered by USB. I'll try to replace the buck-boost with a SOT (small outline transistor), it should be cheaper and easier to use.
I'm very happy with the result of this PCB. Even with this Frankenstein look, because of the replacement of the mosfets.
Of course, I would like to redo it more cleanly, now that I've understood the mistakes I made, but in doing so I would be very tempted to change something: I would love to have a silent system. But that would mean a smaller pump (5V) and therefore less suction, which means a smaller inflatable, a smaller device. I keep this option in mind, as a V2 of this project.
An important part of my final project is the inflatable part, which will help us understand the electrical activity of plants. This part should produce organic movements that react to a flow of air.
So I have to produce a silicone part that has an air pocket inside that could be inflated and deflated by pushing and pulling a flow of air inside through two pipes (in and out).
Strategy
The strategy that Eduardo Chamorro (one of my instructors) and I came up with is to build a mold using a piece of laser-cut acrylic to cast a first layer of silicon and then redo another layer while inserting another piece of acrylic that will allow us to preserve an air pocket inside the cast silicon piece.
Design a structure that can be lasercutted
Plan which parts will be added and/or removed during the casting process
Keep in mind how the air will circulate
Design
I designed the mold using Freecad and parametric values. The shape of this inflatable part is given by the overall structure.
Fabrication
Lasercutting
The cutting of the 5 mm thick acrylic sheet was very fast. Indeed, nothing fancy here, only cuts.
Settings
95% power
0.45 time
20000 Hz frequency
Corrections
I used the exact diameter of the pipes (the two holes in the middle disk) in my design and, of course, the pipes did not fit into the laser-cut holes. I had to redo them using the hand drill. This step was actually quite fast and accurate, but it could have been avoided in the design. Practice makes experience, experience makes practice.
Now that my mold is ready, let's go to the next step and play with silicon.
Casting
The idea here is to cast two times, by flipping the part over and changing the position of the central disk between the two steps.
I will use the same silicon, Easyplat 00-40, that I used to make the mold of my oloids during the week of molding and casting. Much of the following process has already been explored during the that week, I invite you to read it for more information.
The first step consists of using the mold with the disc fixed in the center and filling it with a layer of silicone.
As you can see, I used two acrylic sticks pushed into the holes of the disc to make sure that the silicon cannot block future air passages.
Second step
Then for the second step, I removed the disk in the middle, fliped the silicon piece and put the disk in the center again. I then poured a thin layer of silicon on top of it. Remember that the idea is to capture the acrylic disc in the middle of the silicone while keeping empty spaces for air circulation.
Unscrew the structure and flip the mold
Clean everything
Place the disc in the center, on the first silicon layer.
glue the holes (with transparent adhesive tape)
Calculate the new volume to be cast, and prepare the silicon mixture
Pour and try to reach a thickness of about 1.5 mm.
The ideal thickness is between 1 mm to 2.5 mm. The thinner it is, the flexer but also more fragile it will be. This step may require several tests.
Corrections
Silicon is a very tolerant material. Errors can (almost) always be corrected.
If the mixing has not been done correctly, the silicon will not stick. It is then easy to peel it off and redo the step that went wrong.
If the top layer is too thin and therefore fragile, simply cover it with another thin layer to complete it. The two layers of silicon will merge and leave no trace.
If the top layer is too thick and you see it before it is completely dry, peel it off, clean it and start again.
Mistakes encountered
When mixing the silicon, I calculated the volume needed to fill the mold without subtracting the internal parts, I ended up with a waste of silicon (which is expensive)
I used white masking tape in the first version I did, which was much too visible and therefore un-aesthetic
During my first attempt, the second layer of silicone was too thin, the inflation worked but I tore the membrane
Result
I'm super happy with the result. The silicon piece I produced got the texture I imagined and can easily be inflated/deflated. Here I'm blowing to activate the movement but the idea is to then automate this movement with an air pump.
Conclusion
The fabrication of inflatables using laser-cut parts and silicone makes it possible to think differently about what is feasible in a Fab Lab. Playing with air and silicon almost directly gives an organic movement that can then bring any type of project to life.
I think this kind of technique, and the whole field of soft robotics, needs to be explored further. It can bridge the gap that separates us humans from the messages conveyed by technology.
The structure was designed with Freecad and made to be milled with a CNC machine on a 9mm thick plywood panel. The upper disk supports the inflatable part and the lower disk supports the electronics. A 3D printed part ensures the desired angle of the structure.
Design
The keys, the parts that lock the structure, are not straight but have a small angle to be able to drive them into the other part.
Fabrication
Prototype
I wanted to see my project out of my computer to make sure I was designing it with good proportions before I cut it with the CNC on a wood board. To do this, I lasercut a tiny prototype on cardboard.
Cardboard is the cheapest and most environmentally friendly material available in Fab Labs, making it the best material for prototyping.
That step gave me the "green light" to fabricate my project real size.