The objective of this project was clear: making an object float, by means of magnetic levitation and controlled by an Arduino Nano.
But there were some requirements, too.
First, I wanted an object of a certain size and weight, floating in a very stable way (no jitter, no oscillations,...).
Additionally, safety was a concern (the main magnet has a 24 Volt and 25 Watts rating, producing quite some heat).
And last but not least: the end result had to be 'nice', earning its place in my living room !
I finally came up with an earth globe, not only floating but rotating at a fixed pace as well, illuminated by two digitally controlled led strips.
Magnetic levitation principleOne way to achieve magnetic levitation is to position an electromagnet above an object (in this case, an earth globe) which contains a very strong magnet. By constantly measuring the vertical position of the object, we can vary the magnetic force exerted by the electromagnet on the object's magnet to keep the object in place. This happens much in the same way as one driving a car on the motorway: by constantly measuring whether the car drifts to the left or to the right, the driver can stay on his / her lane by applying small corrections only. This mechanism is called a 'control system'.
Without entering into details, what makes it difficult is that this magnetic levitation control system is not a 'linear' system at all. For instance, the relationship between current applied to the electromagnet and force exerted (on the globe's magnet) is quadratic. Moreover, this force varies with the distance between electromagnet and magnet (which seems logical). But this relationship also is quadratic !
That means that the 'parameters' (we'll talk about that in a moment) used to keep the globe's position stable are critical. Changing the globe's weight, the magnet, the electromagnet, the position of the sensor,... will probably result in a highly unstable globe, and you will have a hard time finding the correct parameters (which will be trial and error, I'm afraid) to obtain stable operation again. Indeed, this non-linearity also makes it more difficult to apply mathematical methods to calculate these parameters.
So, a word of advice in case you would like to build this yourself: try to obtain the same lifting electromagnet and same permanent magnet I used, and aim at a globe weight (including magnets) around 140 grams.
Globe lifting control systemSo, how does it work ? Well, I'll try to explain in an easy way - and in the meantime, please keep an eye on the drawing below.
First, we need to determine the globe's vertical position. This position is measured by a hall-effect sensor, a tiny device which measures the strength of the magnetic field (produced by the magnet mounted inside the globe), converting it to an electrical voltage. If the globe comes closer to the sensor, the magnetic field strength will increase and the output voltage will change. In our case, because of the way the magnet's north and south poles and the hall-effect sensor are positioned, this voltage will decrease. Voltage will increase again if the globe moves further away from the sensor. So, the sensor effectively converts a position to a voltage !
This (small) voltage (typically between 100 and 300 millivolts at the operating point, depending on settings) is amplified by Opamp IC8B (see schematic) and is then converted to a 10-bit number by the Arduino ADC. Welcome in the digital world ! (The amplification by Opamp IC8B is a necessary step, to better capture small variations, taking into account the 10-bit Arduino ADC resolution)
Note that, from a control systems perspective, the ADC conversion can be considered a second 'amplification' step: the ADC scales an input voltage to a number from 0 to 1023. It is perfectly fine to consider this number as the digital representation of a (scaled) voltage.
As a next step, the program will subtract this number (representing a position but expressed in millivolts) from the desired position (called 'setpoint') which should also be expressed in millivolts and appropriately scaled (we can only subtract values expressed in same units).
The subtraction gives us the deviation from the desired globe position (we call it 'error signal', because it's zero when there is no deviation). This deviation is (again) expressed in millivolts.
Forgetting for a while about the 'integrator' and 'differentiator' boxes in the drawing (I'll come to that in a moment): we have a third amplification step which is performed digitally this time. The amplification factor ('gain') is stored as a constant in the Arduino program.
Note that the total gain, starting at the input of Opamp IC8B, is the product of the three amplification steps described - whether these steps happen in the analog or digital world is not relevant at all ! And this gain is really important, because a higher gain makes that a same (small) controller input (here: deviation in position) produces a higher output (here: a pulse with a higher duty cycle - we'll come to that). Imagine this total gain would be very small: not a lot would happen, right? More in general, with the gain too low or too high, the system risks to become highly unstable, so we need to choose it carefully.
Anyway, the amplified error signal is then fed to Arduino Nano Timer 1 to generate a pulse with a specific duty cycle (Pulse Width Modulation): if it's zero, then the duty cycle will be zero as well. If it's 999, the duty cycle will be 100%. The pulse duration is fixed at exactly one millisecond. See the oscilloscope screen capture below (blue channel).
The pulse produced, will determine the 'ON' time of the electromagnet (using power transistor Q1 - we're back in the analog world now) within that single millisecond. This will increase or decrease the 'average' force exerted by the electromagnet on the globe's magnet within that millisecond and as a result the globe's position will change.
As you might have guessed, this whole process is repeated every millisecond (interrupt driven). Every cycle is started by reading the ADC output to obtain the hall sensor voltage. If the globe is in a stable, floating position, this voltage will be close to the setpoint voltage (in this example: around 1000 millivolts, which is 10 times the hall-effect sensor output). As with any control system, this voltage will fluctuate a little: if it wouldn't, we wouldn't need a controller. Check out the oscilloscope screen capture (yellow channel) and verify that every millisecond the globe is falling to the ground for approximately 500 microseconds and then moving upwards again towards the electromagnet ('up' is actually 'down' on the oscilloscope and vice versa. Sorry for that).
And what about these integrator and differentiator boxes ? Well...
- Differentiating the error signal with respect to time and adding this term to the error signal provides a faster reaction to quickly changing globe positions (the effect of this term will be smaller for slower changing globe positions). The differentiator time constant Τd (which is also a parameter in the Arduino program) controls the weight of this term. It is the most critical of all parameters: it must be carefully chosen to obtain stability !
- And why do we integrate the error signal (with respect to time) ? Well, imagine the system is in rest and the actual globe position corresponds to the setpoint: this reduces the error signal to zero, as well as the PWM output (pulse with zero duty cycle) and the electromagnet's force exerted on the globe. But this situation is impossible: because of the globe's weight, it needs a counterforce. So, the globe's position in rest will be a little lower than indicated by the setpoint (which leaves us with a non-zero error signal and, consequently, a non-zero PWM signal and a counterforce). This is called 'static error'. Now, if we integrate the remaining error signal, the controller (PWM) output will slowly increase, the globe position will approach the desired position, the error signal will become zero and integration will stop... exactly when the globe is where we want it to be. The integrator time constant Τi is, as you will have guessed, another parameter in the Arduino program. It is less critical than Τd.
Conclusion: 4 parameters are important here. Setpoint, gain, integration time constant and differentiation time constant. You'll find these constants defined in the Arduino program.
And why is the sampling time 1 millisecond ? Why not 10 milliseconds ? In free fall, an object initially at rest will fall about 5 micrometers in 1 millisecond. In 10 milliseconds, this increases to 0.5 millimeter (x 100). Not exactly a stable situation as perceived by the human eye and probably a lot more difficult to keep the system stable from a control system perspective. That answers it !
Electromagnet assemblyThis assembly contains everything we need, except for the coils used for globe rotation and the led strips.
The electromagnet is quite powerful and produces a fair amount of heat. To keep everything safe, a quite large heatsink is placed on top of it. Additionally, a temperature sensor is fixed to the heatsink. It will be used to continuously control temperature.
Above the heatsink comes a wooden plank (used to 'hang' the assembly in the wooden lantern without the need for any nuts or bolts). Just underneath, notice an aluminum corner profile to keep the plank in position. Threaded spacers connect the plank with the base of the heatsink.
Underneath the heatsink, using the same holes (which you will have to drill, of course) we have another set of threaded spacers arriving at a plexiglass plate, holding the sensor we use for vertical position measuring (hall-effect sensor).
The plexiglass measures 90 x 105 mm. The back edge extends 67 mm from the hall-effect sensor (electromagnet center) position. Note that this last dimension is important as this is where the 3D assembly holding the globe rotation hall-effect sensor will be attached to the plexiglass. We'll discuss this later.
Also, you will have to cut the plexiglass plate and drill holes in it. Carefully measuring and making a few sketches first, will prove to be a big help !
Important: make sure that the spacers (and the bolts underneath the plexiglass) are not magnetic.
Lifting hall-effect sensorSeveral ways exist to accurately measure the distance between globe and electromagnet - more correctly, to measure the deviation between desired position ('set point') and actual position. A common method is to use a light source at one side of the globe and a light detector at the other side. But in our case, as said, we use a hall-effect sensor measuring the strength of a magnetic field (which depends on the globe position) and converting this to an electrical voltage.
Actually, this sensor will not control the position of the globe with reference to the electromagnet, but with reference to the sensor itself. But because this sensor is fixed in position, this will ultimately determine the setpoint: the position of the globe with reference to the electromagnet.
As said before, the vertical distance between electromagnet and sensor (or plexiglass plate) is critical.
With the electromagnet I used (height 40 mm), the plexiglass plate is 58 mm away from the heatsink (which can be obtained by stacking 15 mm and 40 mm threaded spacers, adding a nut in the middle). This gives a 'gap' of 18 mm between electromagnet and hall sensor.
A question you might ask: doesn't the magnetic field of the electromagnet influence the sensor reading ? Well, yes... but this is not a problem, because while we read out the sensor value (every millisecond), the electromagnet is always in the same ('ON') condition (you can verify this by looking at the scope screenshot, above). So, it's attribution is constant and can be discarded.
This hall-effect sensor needs to be placed exactly beneath the center point of the magnet. If not, you will introduce oscillations in the system and your globe will start to 'swing' in a horizontal direction, which is something the electromagnet cannot control. You will have to figure out how to determine that position. When doing it visually, take into account a thing called 'parallax'. Suggestion: once you have determined that spot, put a small dot on the bottom side of the plexiglass. This will then help you position the hall sensor.
The hall-effect sensor is placed into a receptacle with precision contacts, glued to the plexiglass plate (I used Loctite superglue). This allows for easy fine tuning the sensor position because the sensor leads are not soldered. Assuming we're using an SS 495A sensor, the stamped side must be facing down (the Arduino program assumes it).
When gluing the receptacle, make sure the plexiglass plate is clean and dry. We'll deal with the PCB connection later.
Temperature sensorThe temperature sensor continuously measures the heatsink temperature: when it passes a set threshold, magnet and coils will be shut off.
You will need to mount the sensor in such a way that it makes good mechanical contact with the heatsink. How to do that will largely depend on the heatsink used.
We'll deal with the PCB connection later.
The globeI was able to find a globe for use as piggy bank. This has the advantage that there's a big opening at the bottom (and the disadvantage there's a slot for throwing in money, too). In this case we'll use this opening to position a strong permanent magnet (Neodymium, 20 x 20 x 20 mm) inside, near the globe's geographic North Pole. I used double-sided adhesive tape to do the trick. You'll need some chirurgical skills and good thinking to finish the job without having to open (and possibly damage) the globe, but... it's possible.
Make sure you position the magnet with its northpole facing upwards (a cube has six sides, so you will have to do some experimenting). Be careful not to make a mistake here: if you place the magnet upside down, you will not be able to make it work! Best is to use a compass to determine the magnet's north pole: a magnet's north pole attracts a compass needle's south pole: this is the needle pointing in the direction of the earth's geographic South (this looks strange, but the earth's magnetic North is located close to the earth's geographical South and vice versa).
Later on, you will have to reverse the electromagnet wires if the force exerted on the globe's magnet proves to be repelling instead of attracting. But don't worry about that for now.
But we also want the globe to rotate, right ? This will require placement of two additional (smaller) magnets inside the globe. Also these are of the Neodymium type. And again, I was lucky... the globe I use, is made of two plastic halves with an inside border (in red) used to glue them together (I guess). This border is the ideal place to position these two magnets: one at the Greenwich meridian, and the other... at the opposite side, of course (the anti-meridian). Note that I used double-sided adhesive tape.
Important: position the magnet at the Greenwich meridian with its north pole facing up, the other one with its north pole facing down. The magnets will generate a (very small) torque, keeping the globe rotating - but that comes later.
Globe rotation coilsTo create a globe rotation torque, we need a rotating magnetic field. The six coils placed below the globe will serve that purpose.
You will have to make these coils yourself, out of empty spools (I found these on the internet) and 500 turns of enamel-coated copper wire (0.28 mm diameter). Supposing the coils you use will be similar to mine, this will produce a coil resistance around 20 Ohm, which is just fine.
The coils are held in place by a 3D-printed spider with six knobs, providing a tight fit with the center holes in the spools. The spider itself is fixed to a plexiglass plate by two screws.
A two-pin header is soldered to each coil, these headers are all connected to a larger 15-pin receptacle (with 3 unused pins) glued to the spider, right in the middle.
The coils are numbered from 1 to 6 (check out picture 'Globe rotation coils, in lantern') and coil terminals are labeled A and B.
If, looking down at a coil from the top, the winding direction (from first to last turn) is clockwise, we define terminal A as the start of the first turn of the copper winding (close to the center of the spool) and terminal B as the end of the last turn of the copper winding.
If the winding direction is counterclockwise, then we designate the start of the first turn as terminal B and the end of the last turn as terminal A.
The coils will work together in 3 pairs: coil 1 and 4, 2 and 5, 3 and 6. Therefore, we will connect the 2-pin headers (coil terminals) to the receptacle as indicated in picture 'Globe rotation coils: close-up of connections, top view', below. As you can see, the coils 'B' terminals are simply interconnected for each coil pair. The Receptacle pins for the coils 'A' terminals are soldered to the flat cable (6 wires).
At the other end of the flat cable, we will solder a receptacle later. Foresee ample cable length for the time being.
The plexiglass plate, of course, will have to be cut to the desired dimensions, depending on the lantern you use. See picture 'Globe rotation coils, in lantern'.
If everything is correctly connected, at any given moment the vertical magnetic field orientation of three adjacent coils will be opposite to the magnetic field of the other three coils. At regular intervals, this field will rotate by 60 degrees, counterclockwise (looking at the coils from the top). This will create a torque on the globe's two side magnets. Moreover, this rotation will be in phase with what the Arduino program expects. So, it's very important to follow the connection instructions carefully.
To obtain a nicer visual effect, the coils are positioned some 130 mm under the two globe rotation magnets. As we don't need much torque, even with this distance we will be able to keep the globe rotating.
This assembly consists of six 3D-printed parts. Its purpose is to position the globe rotation hall-effect sensor in such a way that it can pick up the passage of the 2 globe rotation magnets mounted inside the globe. Therefore, it allows the hall sensor to be moved horizontally (by moving the 'vertical connector' part forward or backward) and vertically (by sliding the 'hall-sensor housing' up or down in its slot). Once in position, two nuts keep the hall-effect sensor firmly in place.
The hall sensor itself is placed inside the small tube at the lower end of the 'hall-sensor housing'. A precision-contact receptacle is glued to the back side of it (I used Loctite superglue) to receive the three hall sensor leads.
This allows for easy fine tuning the sensor position because the sensor leads are not soldered. Assuming we're using an SS 495A sensor, the stamped side must be facing up (the Arduino program assumes it).
Mounting the hall-effect sensor is a simple task: just bend the 3 leads in a 90° position, slide the sensor inside the small tube (from back to front) and, using tweezers, place the 3 sensor leads in the receptacles with precision-contacts.
Globe rotation control systemAs explained already, the rotation coils beneath the globe will exert a tiny torque on the globe (because of the two small magnets mounted inside the globe). This torque is so small, that it will only keep the globe's rotation synced with ('locked to') the rotating magnetic field (caused by the six coils) if the two speeds (of magnetic field and globe) are not too far apart.
As a side note, it is certainly not helping that we also have the earth magnetic field trying to turn the globe into a compass: indeed, if the coils are switched off, the globe will always position itself with its two magnets pointing to the earth's magnetic North and South Pole. The fact that the rotation magnets are positioned with their poles facing up and down does not prevent that, because the earth magnetic field lines do not run horizontally - in most geographical areas, they have a vertical component !
Anyway - we need some type of control system - but nothing like the lifting control system described earlier (which is based on pure control theory).
First thing is, the controller needs to have knowledge about the globe's rotation speed. We won't measure this speed directly (although methods exist to do that) but we will measure the time of each globe rotation. As you will have guessed, the rotation hall sensor provides this input. It only detects passage of the 'Greenwich anti-meridian' magnet - the other rotation magnet (at the Greenwich meridian) passes quietly.
The circuit around opamps IC8A and IC8D takes the rotation hall sensor as input (oscilloscope screenshot - blue channel) and provides a pulse (yellow channel) which is then further shaped by a Schmitt trigger (IC8C). The Schmitt trigger output (not shown) is sampled by the Arduino Nano and used as controller input.
If the globe rotation speed is currently too low (lower than the desired speed), the rotating magnetic field speed will be set to a speed slightly higher than the current globe rotation speed (and vice versa). This process is repeated at each full turn of the globe, until the globe rotation speed reaches a small band around the desired rotation speed. Within that band, the control system is switched off and the rotating magnetic field speed is fixed to the desired rotation speed, for once and for all. Globe rotation is now 'locked to' or 'synced with' the rotating magnetic field.
The control system described here will only work if the phase of the rotating magnetic field is set correctly (with reference to the rotating globe). The program takes care of that, but this requires correct wiring of the coils and correct relative positioning of the coils in the horizontal plane with respect to placement of the hall sensor (see picture 'Globe rotation coils, in lantern').
Led stripsBecause a globe floating and spinning in the dark is not much of an attraction, two digital led strips are added as a final touch. Each RGB led is individually controllable and only four wires are needed: ground, 5 Volt, data and clock.
These led strips are sold 'by the meter' and you can easily cut them to the desired length. In this case, each led strip contains 8 LEDs. The type I used also has a very convenient adhesive tape at the back side, making it quite easy to position the led strips and fix them in place (probably you'll need a few small wooden rails as well).
The clock lines will be tied together later, and the data lines as well (we will send the same data to the two led strips). For now, just solder a 4-wire flatcable of sufficient length to the input side of each led strip (the data flow is clearly indicated by arrows, as you can see in the picture). As a strain relief and to keep the flatcables in place, easiest is to glue them to the lantern. We'll deal with the other end of these two flatcables later.
Important: to keep current consumption low, only 8 of the 16 LEDs will be used and even not at full power. This is quite sufficient to obtain nice visual effects. If each RGB led (rated at 60 mA) would be switched on simultaneously and at full power, this would draw almost 1 Ampere. We will use a fraction of that.
Note: the l293D half bridge providing power to the led strips has a maximum rating of 600 mA.
Programming the Arduino NanoNow is a good time to program the Arduino Nano.
Download and compile the code, connect your Nano via USB to your computer and program your Nano. That's all for now.
PCBA two-layer PCB hosts all electronics, except the sensors, the electromagnet, the coils and the led strips. The PCB has been designed with great care, trying to observe good practices like making adequate use of power planes and separating as much as possible analog and digital component areas (minimize noise introduced by digital logic presented to the analog circuits - especially the stability of the floating globe will depend on signal variations of less than 0.5 millivolts).
The board contains a number of extra connectors, making available most of the Arduino Nano pins as well as a few other signals. This facilitates using the board for prototyping activities beyond the scope of this project.
The files ('Gerber files') you will need to order this PCB are provided in the CAD section.
What follows is not a complete explanation of the PCB schematic. It is merely meant to give you some insight in the way the board works.
- The board uses Arduino Nano port D (6 bits wide) as a data bus. It provides the data for the coils, the LEDs, the led strip, the optional LCD,... and reads the switch and button states.
- IC10 decodes a 3-bit address (provided by Arduino port B bits 0, 4 and 5) into 8 I/O selection lines. Four of these selection lines will select either IC2 flip flops to write data to (to control coils), IC3 flip flops (to control leds etc.), IC4 buffers (to read switches and buttons) or the led strip. Unused selection lines are wired to an expansion connector (SV5). Note that the (optional) LCD chip select is directly wired to an Arduino Nano pin (check the code), because it is accessed via the LiquidCrystal library which does, of course, not use a decoder chip to select the LCD.
- IC5 and IC6 each contain 4 half-bridges, which are typically used to control motors etc. We use IC5 and half of IC6 to drive the six coils. The two remaining half bridges of IC6 are used to supply voltage to the led strips (these are not directly connected to Vcc) and let the software control the red led.
- Opamps IC9A and IC9D form some kind of a 'hardware watchdog'. If the software fails to regularly switch the signal at its input from high to low and vice versa, its output will come low, signaling an error condition, effectively shutting down electromagnet, coils and led strip and switching on the red led.
- Opamp IC8B amplifies the lifting hall sensor by a factor 10. Important: by changing the values of resistors R10 and R12 from 100K to 150K, you can increase the amplification to 15 (which provides a greater ADC accuracy). If you do so, you must change the corresponding #define statement in the Arduino program accordingly. This signals the program that a few constants need to be adapted (the digital gain, for instance).
#define highAnalogGain 1 // 0: analog gain is 10, 1: analog gain is 15 (defined by resistors R9 to R12)
- Opamps IC8A, C and D are used to shape the signal coming from the rotation hall sensor.
- Opamp IC9B is used as an inverter and IC9C is not used at all.
Parts are clearly indicated on the picture below, on the board itself and on the schematic drawing attached. Start by placing all resistors, diodes and small capacitors. Then solder the IC sockets (female headers for the Arduino Nano) which will save you a lot of trouble in case a chip has to be replaced. When done all that, place and solder the remaining components. Do not place the actual chips yet. By the way, I used 74HCT... logic chips because of the low power consumption, but 74LS... chips will work as well (IC1 to 4, IC10).
The last part to mount is the VMA404 step down converter. It's mounted vertically, components facing outward, as can be seen on the picture below. The terminals are clearly labelled on the main board and on the step down converter board, so you can't really make a mistake. In- and Out- refer to GND in and out, In+ refers to 24 Volt in and Out+ refers to, obviously, the voltage output by the converter. Use sturdy blank wire for the connections to ensure proper rigidity.
For the time being, do not yet connect terminal Out+. First, plug in the external 24 Volt wall power supply, adjust the converter's trimpot to obtain an output voltage of 7 volt and remove external power again. This voltage setting will guarantee proper functioning of the board without dissipating excess power.
At this time, connect terminal Out+ as well, reconnect the external power supply and use your multimeter or oscilloscope to run a few tests verifying that you obtain 5 Volt where expected, as indicated in the schematic.
If this test succeeds, you can now place all chips, including the Arduino Nano.
Do not physically attach the PCB to the electromagnet assembly yet (using spacers) - in case there is something wrong and you would need to do troubleshooting, this will save you some time.
PCB connectorsWe will need a connection to the three sensors (temperature and two hall-effect sensors), the electromagnet, the coils and the led strip (and we'll need power, of course).
- Power is supplied by an external 24 Volt wall power adapter, plugged into the PCB DC connector. It's a good idea to check the polarity first: 24 Volt (+) is to be delivered to the center pin.
- Electromagnet: you will have to experiment (later) to determine the polarity of the two wires. If the force exerted will be repelling rather than attracting... you'll need to switch the wires.
- Coils: solder a 10-pin header connector to the 6-wire flatcable connected earlier to the coils (leaving 4 terminals free). First, match the signal names in figure 'Globe rotation coils: close-up of connections, top view' (in section 'Globe rotation coils') with the identical signal names in the picture below to know which wire to connect to which header pin. These signals are labelled 'COILPAIRn-x' with n = 0 to 2, x = A or B.
- Led strips: the PCB led strip connector has 5 pins. The pin closest to the DC power jack, pin 1, delivers 5 Volt. Pin 2 and pin 3 are two data lines. Pin 4 delivers the clock signal and pin 5 is GND.Use a matching header connector to connect the flatcables coming from the two led strips to the PCB connector (tying together GND, 5 Volt, data and clock wires, respectively).Note: as an alternative, you can decide to keep the two led strip's data signals separate, connecting the two data lines to connector pins 2 and 3, respectively. Currently, these pins supply the same data, but this would allow controlling the two led strips separately (by changing the program, of course).
- Sensor connectors (schematic: J3, J4 and J5) each expect Ground, 5 Volt and sensor signal output wires to be connected to the same respective connector pins (see picture below). So, if you make a mistake later while connecting the sensors to the PCB, nothing will breakdown (but your board won't function, of course).I recommend not to use a connector for the lifting hall-sensor (connector J3) but to solder the wires directly to the board, not making them longer than needed, because the voltage variations measured are small (less than 0.5 millivolts).Beware: the sensor pin arrangement differs from the corresponding connector pins. Check the sensor's datasheets !
Of course, I can only give you some general guidelines, but I would suggest using this roadmap for testing:
- Test 1: verify communication (over USB) with the serial monitor on your PC. Make sure all DIP switches are in the OFF position and verify that the serial monitor's baud rate corresponds to the baud rate set by the Arduino program (standard 1000000 baud - preferably do not change it). Plug in the USB cable (you don't need to power on the PCB board yet and you don't need to make any other connections for now) and verify that the Arduino responds with a message starting with "Type + or - to change parameter shown, E to edit value,..." after reset.
- Setup: in the serial monitor, type 'A' + ENTER. Arduino will respond with a list of parameters and values. Check that the rotation time set ('rot time') is 12 seconds and the vertical globe position ('vert pos') is set to 1000 millivolts (or 1500 millivolts, if you changed the analog gain to 15 - see section 'PCB'). If it's not, refer to section 'Serial communication' to set these values.
- Test 2: for this test, forget about globe rotation and led strips for a while. Just connect the temperature sensor (if you don't, the board will detect a 'high temperature' and will not allow powering the electromagnet). Connect the lifting hall sensor and the electromagnet wires as well. Power on. You should see the green PCB led flashing. Wait 5 seconds. The blue PCB led should start blinking (a complete explanation about led colors and meaning is given further on). Now, move the globe (with the big lifting magnet) slowly upward, in the direction of the lifting hall sensor (and the electromagnet) until it touches the plexiglass plate. Then move it away from the sensor, in a downward direction. This enables the electromagnet and the lifting control system. Bringing the globe closer to the magnet again, you should now observe an attracting force between electromagnet and lifting magnet. If you observe a repelling force, the two electromagnet's wires need to be switched.
- Test 3: try 'hanging' the globe into position (procedure explained in next section).
- Test 4: power off, connect the rotation hall-effect sensor as well and power on. Hang the globe into position again and give it a slight turn (counterclockwise). Connect an oscilloscope to IC8A pin 9 (Schmitt trigger input) and verify that the waveform you see looks like the yellow waveform in figure 'Greenwich magnet passage: pulse shaping' in a previous section. If it's not, then try to obtain this waveform by adjusting the horizontal and vertical position of the rotation hall sensor (loosening the two nuts - see section '3D assembly).
- Tune Schmitt trigger output: connect oscilloscope channel 2 to IC8C pin 8 (Schmitt trigger output). By adjusting trimpot R8, set the input threshold voltage (scope channel 1) to 0.8 Volt (more or less). Supposing you gave the globe a rotation speed between somewhere between 10 and 15 seconds, this should produce a pulse between 0.5 and 1 second, each time the 180° meridian passes the rotation hall sensor.
- Test 5: power off, connect the coils as well and power on. Hang the globe into position again and give it a slight turn (counterclockwise). The rotation control system should now kick in. After some 30 seconds, the PCB led should go out, indicating that the set rotation speed is obtained.
- Test 6: now, connect the led strips. Then, use serial monitor commands (explained in a next section) to set led strip cycle 5 (cycle through all colors) and led strip timing 1 (fastest). Verify that the led strips function properly (remember that not all LEDs are used).
Refer to next sections for more detailed info on how to handle the globe, the status LEDs and serial communication.
Positioning the globeWhen you power up the system, you can immediately 'hang' the globe in position (some 35 mm below the electromagnet, or approximately 15 mm below the plexiglass plate holding the lifting hall-sensor): just watch the RGB status led mounted on the PCB. If it lights up steady green, you're at the correct height. If it blinks (green), you're either too high or too low. it needs some practice (not too much, though) but it should be fairly easy.
Once the globe floats, give it a slight turn (counterclockwise) and wait... depending on the initial rotation speed you gave it (which is not critical), after some time, the globe will attain its set rotation time (standard 12 seconds).
Status LEDLed is off : the globe is floating and globe rotation is locked (synced with the rotating magnetic field created by the coils).
Led is ON (green or blue):
- green led flashes approx. 4 times a second: globe is not within a narrow 'floating' range. While positioning the globe, use this to guide you to the correct distance between globe and electromagnet.
- green led is on continuously: globe is floating but no Greenwich position sync (passage of Greenwich anti-meridian magnet) detected yet. Rotation coils switched off.
- green led is flashing rapidly (approx. 16 times per second): globe is floating; a first rotation time measurement is underway. Rotation coils switched off.
- blue led is on continuously, but with brighter (green) flashes: globe is floating but globe rotation is not yet locked. A green flash indicates the direction of the rotating magnetic field is changing. A blue flash indicates the Greenwich magnet is currently passing the rotation hall sensor.
Led is blinking blue: lifting magnet and coils are currently switched off because of an error condition. The number of consecutive 1-second blinks (followed by a pause) indicates what caused the error condition:
- 1 blink: dropped globe for more than 5 seconds (globe probably fell down or was taken away, or no globe detected yet after power on).
- 2 blinks: sticky globe: globe position too high for more than 5 seconds (globe was probably sticking to the plexiglass plate).
- 3 blinks: average lifting magnet duty cycle was higher than 80% for the last few minutes (a situation that is highly unlikely to occur, but as this concerns safety...).
- 4 flashes: smoothed temperature reading is higher than 65° Celsius.
To enable the system again, bring the globe close to the plexiglass plate holding the lifting sensor (in an upward movement), then move it away from the plexiglass plate again (downward movement). The led will now turn green again and you'll have (again) 5 seconds to bring the globe back into its floating position.
Led is red: if, because of a malfunction or program error, the program stops producing pulses on the input of the circuit around Opamps IC9A and IC9D (acting as a hardware watchdog), lifting magnet and coils will be disabled and the led will turn red.
Serial communicationBy using the Arduino serial monitor (or other properly configured 'terminal' software) via USB, you can print certain information and change a few settings.
The following information ('parameters') can be printed:
- Currently selected rotation time (setting)
- Actual rotation time
- Rotation time sync error: the accumulated globe rotation time error (when locked)
- 'Globe rotation locked' time. Reset when globe rotation is not locked
- 'Globe is floating' time. Reset when globe is not floating
- Temperature (degrees Celsius)
- Lifting magnet average duty cycle (%)
- Globe vertical position: reference (setpoint) in millivolts (setting).Note that setpoint values refer to values as read by the ADC, so they are scaled to 10 (or 15 - see PCB section) times the hall-sensor output for the set vertical position
- Smoothed average of the absolute value of the globe vertical position error in millivolts as read by the ADC
- Current value of the integration term, calculated by the globe lifting controller
- Average phase between rotating magnetic field and rotating globe (when locked). Can be used to calibrate the phase between rotating magnetic field and globe, set by the globe rotation controller before locking is obtained
- Average duration of the 'ADC conversion complete' ISR routine in microseconds (smoothed)
- Average processor load (smoothed)
In addition, each status change will be printed (e.g. the selected rotation time is reached).
Use the following commands to print parameters and their values or change settings (sending a 'Carriage Return' is not needed, although you might have to press Enter to send your command to the Arduino, depending on the terminal software you use).
- '+' or '-' : print next or previous parameter. When in edit mode: select next or previous parameter value from list.
- 'E': edit current parameter. When in edit mode: end editing and save currently selected parameter value in EEPROM memory. Only available for changing rotation time and fine-tuning globe vertical position, no function for other parameters.
- 'C': cancel editing without saving. No function when not in editing mode.
- 'S' to start or stop showing live values. This will start or stop printing the actual value of the currently selected parameter regularly to the serial monitor (for instance the time it took to complete the last globe rotation). Does not apply to settings.
- 'A': print all parameters at once, as well as current led strip settings.
Additional commands:
- '?': print available commands (help).
- 'T': print time stamp (time in milliseconds since last reset - a real time clock is not available).
- 'LC0' to 'LC5': select a led strip cycle. 0 = Off, 1 = constant brightness white, 2 = constant brightness magenta, 3 = constant brightness blue, 4 = fade between white and blue, 5 = smoothly cycle between all colors (from blue to cyan, green, yellow, red, magenta, and blue again,...). Selection is stored in EEPROM memory.
- 'LT1' to 'LT4': change led strip cycle time. 1 = fastest (minutes), 4 = slowest (hours). Note that the timing is not relevant for all led strip cycle types. Selection is stored in EEPROM memory.
- 'R0' and 'R1': (step) response. Applies a step change ('R1') or no step ('R0') to the globe vertical position reference (setpoint) and then sends the measured response (actual globe vertical position) and controller output (PWM duty cycle) to the serial monitor where it can be logged for evaluation in excel etc.The step applied ('R1' command only) increases the vertical position setpoint with 100 ADC steps. Expressed in millivolts at the hall sensor output: 100 ADC steps * (5000 millivolts / 1023 ADC steps) / 10 = 49 millivolts (if the analog gain is set to 10 - see section 'PCB').The Arduino starts sending data to the serial monitor, 1 second before the step is applied ('R1' command) and continues sending data for 20 seconds.Every millisecond, a line containing three values is sent, the values being separated by a semicolon. Value 1: millisecond (1 to 20000)Value 2: lifting hall-effect sensor value read (in ADC steps)Value 3: controller output (PWM duty cycle as a value from 0 to 999 = 100%)
A few notes on the use of the 'R0' and 'R1' commands:
- The 'Rn' command requires a high baud rate: the baud rate set by the program is 1000000, so this shouldn't be a problem.
- The value of the integration term (see section 'Globe lifting control system') including extra accuracy digits (see section ('Arduino Nano code') at the start of the 'Rn' output (first millisecond) is printed to the serial monitor as well. This allows 'replaying' controller calculations in excel based on logged hall-effect sensor values (in ADC steps), verifying that the calculated controller output in excel is identical to the logged controller output.
- When sending step response data has ended, the globe vertical position is gently brought back to its original setting.
- Do not use this command while you are showing Live values (see 'S' command)
It is possible to connect 4 buttons ('+', '-', 'E' and 'C') directly to the Spinning Globe board PCB, as well as a 16 x 2 character, Arduino compatible LCD display.
This allows you to review the same live values and change the same settings as by using the serial monitor.
The LCD displays status information on the top line (e.g. "E! dropped globe") and the currently selected parameter (see section "Serial communication") on the bottom line.
Note that led strip settings cannot be changed using the hardware buttons. They are not displayed on the LCD either.
Connecting ('make' contact) buttons: use connector SV2.
- '-' button: connect one pin to signal 'SW0' (SV2 pin 2), other pin to GND
- '+' button: connect one pin to signal 'SW1' (SV2 pin 4), other pin to GND
- 'E' button: connect one pin to signal 'SW2' (SV2 pin 6), other pin to GND
- 'C' button: connect one pin to signal 'SW3' (SV2 pin 8), other pin to GND
Pressing these buttons will have the same effect as sending the equivalent commands using the serial monitor (or other terminal software).
Important: make sure DIP switches 2 to 5 are all in the OFF position. These switches are connected to signals 'SW3' to 'SW0', respectively, in parallel with these 4 buttons. Setting a switch in the ON position will tie the corresponding signal to GND, making the corresponding button inoperable. Moreover, if one of these switches is in the ON position right after reset or power on, the system enters programming mode (see further).
Connecting an LCD: use general-purpose connector SV5 to connect an optional 2 x 16 character LCD based on the Hitachi LCD controller.
- Data is sent to the LCD using a 4-bit bus
- The LCD is working in write-only mode: wire the LCD Read/Write pin to ground
- Connect LCD data bits 4 to 7 pins to signals 'D4(PD4)' to 'D7(PD7)' (Arduino port D bits 4 to 7, see schematic)
- Connect the LCD RS (register select) pin to signal 'D3(PD3)' (port D bit 3)
- Connect the LCD enable pin to signal 'PB2' (Arduino port B bit 2)
- Properly connect power, LCD contrast and backlight LED pins (using resistors as specified in the LCD datasheet - resistors not provided on the PCB)
It is possible to set globe rotation time, fine-tune globe vertical position setpoint, or set led strip cycle and timing without using the serial monitor or buttons / LCD.
Procedure to enter and exit program mode:
1. while power is OFF, indicate which setting you want to change with DIP switches 2 to 5 (signals 'SW3' to 'SW0'), as outlined below. Do not use any other switch setting. Note that DIP switch 1 (signal 'SW4') is not currently used.
- ON ON ON ON: change led strip settings
- ON ON ON OFF: set rotation time
- ON ON OFF ON: fine-tune globe vertical position reference
2. Power ON. Then, while power is ON, set the DIP switches as indicated below.
=> If initial switch setting isON ON ON ON: set a ledstrip cycle and timing, as follows:
- ON ON ON ON: switch off led strip
- ON ON ON OFF: constant brightness white
- ON ON OFF ON: constant brightness magenta
- ON ON OFF OFF: constant brightness blue
- ON OFF (T1) (T0): fade between white and blue
- OF ON (T1) (T0): cycle between all colors (blue, cyan, green, yellow, red, magenta, blue,...)
- other switch settings do not affect the last set led strip cycle and timing
(T1) (T0) sets led strip timing:
- ON ON: timing 1 (fastest)
- ON OFF: timing 2
- OFF ON: timing 3
- OFF OFF: timing 4 (slowest)
=> If initial switch setting is ON ON ON OFF: set a globe rotation time, as follows:
- ON ON ON ON: globe rotation OFF ('compass' behavior)
- ON ON ON OFF: 12 seconds
- ON ON OFF ON: 9 seconds
- ON ON OFF OFF: 7.5 seconds
- ON OFF ON ON: 6 seconds
- ON OFF ON OFF: 4.5 seconds
- ON OFF OFF ON: 3 seconds
- ON OFF OFF OFF: 2.4 seconds
- other switch settings: globe rotation OFF
=> If initial switch setting is ON ON OFF ON: fine-tune globe vertical position setpoint (in millivolt units), as follows:
- ON ON ON ON: 1000 millivolts
- ON ON ON OFF: 1200 millivolts
- ON ON OFF ON: 1400 millivolts
- ON ON OFF OFF: 1600 millivolts
- ON OFF ON ON: 1800 millivolts
- other switch settings: 1000 millivolts
Note that setpoint values refer to values as read by the ADC, scaled to 10 times the hall-sensor output for the set vertical position. In case you changed the analog gain to 15 (see section 'PCB'), the values set will be 1500, 1800, 2100, 2400 and 2700 millivolts, respectively.
3. Power OFF. Then, while power is OFF, set switches 2 to 5 to 'OFF' again. The desired setting has already been stored (in EEPROM memory).
4. Power ON again.
Arduino Nano codeThe program is fairly well documented so I will keep it short here.
The program has been designed for speed. Especially, the interrupt service routines (ISRs) do not make use of floating-point numbers. This means that, for instance, the lifting controller calculations are done with (long) integers, increasing accuracy by adding extra 'binary fraction' digits and removing them at the end of the calculation (shifting values left or right by a number of bits).
Note: because of this, care must be taken, if specific constants are changed, that no under- or overflow situations occur.
constexpr int PIDcalculation_BinaryFractionDigits{ 14 }; // added accuracy (binary fraction digits) in PID controller calculations
constexpr int gain_BinaryFractionDigits{ 8 }; // added TTTgain accuracy (binary fraction digits) because of small TTTgain
constexpr int TTTintFactor_BinaryFractionDigits{ 18 }; // added TTTintFactor accuracy (binary fraction digits) because of small TTTintFactor
constexpr int TTTdifFactor_BinaryFractionDigits{ 3 }; // added TTTdifFactor accuracy (binary fraction digits) because of small TTTdifFactor
constexpr int PIDcalc_preliminaryDivisionDigits{ 4 }; // to prevent overflow after multiplication (factor 1/2: keep 1 extra bit for safety)
Also, because divisions take much longer than multiplications (no processor instruction for division), there are none in the ISRs. Example: dividing a value by a constant '5' can be performed by multiplying that value with an integer constant '51' (51 = 1/5 * 2^8) and then shifting right 8 bits.
This results in an average processor load of about 20%, which is measured by the program and can be verified with the Serial Monitor.
Main loopThe main loop is short (everything happens in a number of dedicated procedures) but clearly shows the general structure.
- If an event or user command is available, it will be processed. If not, the program moves on. Events are generated by Interrupt Service Routines. User commands are assembled from the Serial Interface or the (optional) hardware buttons.
- Switch states (read and debounced within the ISR) are used to adapt settings if needed.
- Data (if any) is sent to the serial interface and to the optional LCD.
- If led strip brightness needs to change (indicated by the occurrence of a specific 'led strip' event), serial data will be sent to the led strip.
- As long as there's nothing to do, control will stay in the idle loop. If there are still events to process (event queue not empty), the key buffer is not empty or an interrupt occurred ('Timer 1 overflow' or 'ADC complete interrupt') the idle loop will exit.
void loop()
{
getEventOrUserCommand(); // get ONE event or assembled user command if available
processEvent(); // process event, if available
processCommand(); // process command, if available
checkSwitches(); // if SW3 to SW0 to be interpreted as switches only
writeStatus(); // print status to Serial (and LCD if connected)
writeParamLabelAndValue(); // print label and value to Serial (and LCD)
writeLedStrip(); // write led strip data
myEvents.removeOldestChunk(ISRevent != eNoEvent); // remove event from queue
wdt_reset(); // reset watchdog timer
resetHWwatchDog = true; // allow ISR to reset hardware watchdog
idleLoop(); // return if still something to do or ISR occurred
}
Note: as a general principle, all data transferred to / from hardware over the Arduino Nano Port D 'data bus' (see section 'PCB hardware') is written or read from within the ISRs, because this goes fast anyway, but with two exceptions:
1. Led strip data is sent serially over the same Port D data bus, which takes a few milliseconds (400 bits sent each time the led strip needs to be refreshed) - this would stall the ISR execution. Therefore, the main program loop takes care of sending led strip data. The program ensures there are no 'collisions' when sending led strip data is interrupted by an ISR sending or receiving data using the same data bus.
2. The optional LCD also uses the same data bus, and is also written to from within the main program loop because the standard LiquidCrystal library is used, which has no knowledge of the hardware address decoding logic used on the PCB.
Timer 1 overflow interrupt- executes every millisecond
- provides time base (timer 0 is not used by the program, but it is not disabled)
- resets hardware watchdog
- initiates lifting hall-sensor ADC conversion and enables 'ADC complete' interrupt
- polls globe rotation hall-effect sensor
- reads and debounces switches / buttons and produces keycodes for pressed / released buttons (if connected)
- sometimes executed twice every millisecond: at completion of lifting hall-sensor ADC conversion (every millisecond) and et completion of temperature sensor ADC conversion (every 128 milliseconds)
- reads converted ADC value. If this was a lifting hall-sensor value, proceeds. If it was a temperature sensor conversion, stores the temperature and exits
- performs calculations related to lifting control, rotation control, safety
- sets timer 1 register controlling pulse duty cycle for electromagnet (PWM)
- outputs data for the coils, the LEDs etc.
- calculates led strip brightness levels. Note that serial data will be sent to the led strips in the main program loop (because time consuming)
- sends information to the main loop by using 'events': data is stored in dynamic memory, indicating the nature of the event (e.g. the globe's rotation speed has now obtained the set value) along with some extra information. The main loop will process events and remove them again from dynamic memory (see class 'MyEvents')
- every 128 milliseconds: initiates a temperature ADC conversion (which will trigger a second, but very short, occurrence of this ISR)
I tried to make the instructions as detailed as possible. However, I realize that completeness is something that is very hard, if not impossible, to achieve.
I hope that this project will be a learning opportunity for some and be a lot of fun for others.
Success !
Comments