Skip navigation

Arduino

6 Posts authored by: organtin

In one of my last posts I have shown how to drive a servo motor. Servo motors comes in two flavours: the standard servo motors can be set such that they reach a given position and maintain it; the continuous rotation servo can rotate continuously at a given speed.

 

DC motors are very similar to continuous rotation servo motors, in fact. With respect to the latter, DC motor technology is much simpler and, as a consequence, these motors are cheaper than servos. As suggested by their name, DC motors need just a DC voltage source to run, so there is no need to use any PWM pin. They just have two wires to be connected to a voltage source. The direction of rotation depend on how you connect the wires to the voltage source. Reversing the polarity of the power makes the motor run in the opposite direction. The speed of a DC motor depends on the applied voltage: the higher the voltage, the higher the speed. Motor specifications tell you in which voltage range it operates: below the minimum voltage the motor will probably do not move at all or they move with lot of difficulty; above the maximum voltage you seriously risk to damage it.

 

Some DC motor, as the one we used for our tests, comes with a gear embedded in their body: gears make the motors more powerful, but slower.

 

If, for any reason, you need to control the rotation of a DC motor using Arduino, in principle it would be enough to connect one of the motor leads to the GND pin and the other to any digital pin. Setting the pin to LOW the motor doesn't move. Setting the pin to HIGH the motor runs if 5V is in its range. Given the fact that the direction of rotation depends on the polarity of the voltage, inverting the wires makes the motor rotate in the opposite direction.

 

In some cases, however, this very simple schema cannot work: manifestly it does not work if 5V are not enough to run the motor; moreover, the maximum current that an Arduino pin can provide is 40 mA (but it is recommended to not exceed 20 mA) and it may not be sufficient to run the motor. In those cases you need an external power source. You cannot use Arduino as the power source, but you can use it as the driver of the motor, controlling an external power source used to feed the motor. Suppose, for example, that you need at least 9V to run your motor. You can use Arduino to realise a sort of switch that stays open until a given digital pin is set to HIGH. As soon as that pin flips from LOW to HIGH, the switch closes and the motor is connected to the power source.

 

Of course you can use a relays to this purpose, but we are going to illustrate the use of a transistor to obtain the same result. Understanding how to use transistors is always a good idea, even if you are not going to use them: knowledge is power!

 

Transistors can be thought as a sort of switches. In fact transistors may act both as amplifiers as well as switches, depending on how they are polarised. If you want to understand how transistors work in a non technical manner you can read my article published on 'The Physics Teacher' (Giovanni Organtini, "A flush toilet model for the transistor", Phys. Teach. 50, 221 (2012); http://dx.doi.org/10.1119/1.3694073). In order to understand this post you don't need to understand how transistor work: it is enough to know that they behave like a switch. The switch connects the transistor emitter with its collector and its operation is driven by its base. Consider the following circuit.

 

DCmotorCircuitSchema.png

 

The Arduino board is used just to provide a digital signal on a given pin (pin 9 in the example). This pin is connected to a resistor (whose value is not very important: its purpose is just to limit the current flowing out of the pin) that, in turn, is connected to the base of a transistor. If no current flows to the transistor base, the transistor is said to be in interdiction and acts as an open switch between the emitter and the collector. In our schema the motor is connected to a 9V battery trough the transistor, but you can imagine the transistor being substituted by an open switch. The Arduino ground is in common with the 9V minus lead.

 

As soon as you make enough current flowing into the transistor base, the transistor goes in saturation and acts as a closed switch. In that case the transistor emitter (the lower lead with the arrow in the schema) appears to be short circuited with its collector (the top lead, connected to the motor). In this case the current can flow from the battery and feed the motor that starts running.

 

Below you can see a Fritzing model of the device where you can see that the base of the transistor is its central lead; the emitter is on the left, while the collector is on the right

DCmotorCircuit.png

Below you can see our actual setup.

 

IMG_20160317_183148.jpg

In order to identify the collector, the base and the emitter of a transistor you must refer to its data sheet. There are common rules among producers, allowing you to identify the leads looking at the transistor shape (the one shown in the picture above is called TO-92). However, it is always a good idea to check the pinout on the specific transistor data sheet, because not all the manufacturers may use the same pinout for the same enclosure. The actual pinout, in fact, depends also on the transistor type.

 

There are two types of transistors: NPN and PNP. Transistors are made out of three semiconductor slices and semiconductors comes in two flavours: N-type and P-type semiconductors. In N-type semiconductors the current carriers are electrons, while in P-type semiconductors the current is due to holes (lack of electrons). In NPN transistors the base is made of a P-type semiconductor, while in PNP ones the base is an N-type semiconductor.

 

In the above example we showed how to use an NPN transistor. If you are going to use a PNP one, you should connect the collector to the positive lead of the battery and the emitter to the motor, while the second lead of the motor goes directly to ground.

During the past days I was busy with the preparation of a scientific exhibition that opened on September 25 for the European Research Night. The year 2015 is the International Year of the Light, as declared by the UN, hence the theme of the exhibition is the Light and its scientific applications.

We were working on a Newton's disc, when I came to the idea that one can easily build an electronic version of such a device. A Newton's disc is a disc divided in segments, each of which has a different color. When the disc spins rapidly, the coloured segments fade and the disc appears to be white. The Newton's disc is used to demonstrate that white light is in fact the superposition of light beams of different colours.

The reason for which the disc appears to be white is that images collected by our eyes persists on the retina for a while (1/20 of a second). When the disc spins and you look at a given sector, the coloured light coming from that sector enters you eye and 'remains' on its surface for such short time. However, if the disc spins rapidly enough, the light coming from the same place rapidly changes color and light of different colours hits the same portion of the retina in less than 1/20 of a second. The eye then receives a series of different signals in a short time window and our brain interprets all these signals as a single image providing the illusion that the disc is white.

We can do the same using an RGB LED.

A RGB LED is a LED capable of emitting three colours with different intensities: red, green and blue. Weighting the light of each color allows us to produce light of mostly any color. Instead of mixing three colours at the same time, one can switch on and off each color at the time. If the switching time is low enough you can easily distinguish the sequence: red, green, blue, red, green, blue, etc.. However, if each color is on for a very short time, the eye detect each color in less than its persistence time and interprets the sequence of the three colours as just their sum: white.

RGB LED's exist in two flavours: common cathode and common anode. Both have four pins and contain, in fact, three coloured LED's. In the common cathode pins, the longest one is the cathode to be connected to the ground. Putting a voltage on the other three pins, a current flows through the appropriate LED and the device emits light of the corresponding color. In common anode LED's, the longest pin is the anode to be connected to the voltage source. Connecting the other pins to ground let the current flow.

In this example, I used a common cathode LED with an Arduino board: the longest pin was connected to the Arduino GND pin, while the other to pins 9, 10 and 11 (the PWM ones) by means of a resistor, to limit the current flowing.

Operating such a device is very simple. Just define the used pin as output pins and put them to zero in the setup:

 

void setup() {    
  // set pins as output pins
  pinMode( 9,   OUTPUT);  
  pinMode(10, OUTPUT);  
  pinMode(11,  OUTPUT);  

  // switch off all LED's
  digitalWrite( 9,   LOW);  
  digitalWrite(10, LOW);  
  digitalWrite(11,  LOW);  
}  

In the loop, you can just put each pin to an appropriate value in sequence. For example:

void loop() {
 int i = 9;
 while (i < 12) {
    // switch on a given color
    analogWrite(i, 125);
    // wait d milliseconds
    delay(500);
    // switch off that color
    analogWrite(i, 0);
    // go to next color
    i++;
  }
}

With this code you switch on a color for 0.5 s, then switch it off and repeat the sequence on each color. Reducing the delay to 50 milliseconds you can no more distinguish the single colours. The switching between them is so fast that what you can see is just white light.

The movie above shows my Arduino Newton's disc looping such that the time for which each color lasts is progressively reduced until the eye can no more see the single colours switching. In the end, the LED appears as white.

RGB LED's, in fact, is yet another applications of the Newton's theory of light that can be either interpreted in the wave theory. What happens in an RGB LED is that lights of three colours and different intensities sum up and the result is light whose color is the "sum" of the three beams. Modern TV's and computer monitors, as well as phone screens and similar devices works the same way: the screen is divided in pixels, each of which is made of three tiny light sources of three colours (red, green and blue). Switching on and off with the appropriate intensity each of the sub-pixels results in the image on the screen.

My last post was about the usage of a digital compass. The topic of that post was on the usage of the compass together with an Arduino to obtain the values of the components of the magnetic field vector measured by the sensor.

 

The values obtained with the technique illustrated there are, in fact, just a digitalisation of the "real" values. In other words, what you read with Arduino is just an 11-bits binary number whose value is proportional to the magnetic field sensed in each direction. The sensor used on the PmodCMPS digital compass can measure fields up to  ± 8 G (Gauss, a unit of measure for magnetic fields). According to the datasheet the value provided on each channel is a 16-bit number, whose 5 most significant bits are used as the sign: the reason for using 5 bits and not just 1 is that the ADC on the sensor has only 12-bits. With 11 bits we can get numbers up to 211=2048 (in fact from 0 to 2047). That means that a reading of +2048 corresponds to +8 G, at maximum gain, while reading of -2048 corresponds to -8 G. Playing with the sensor you can see that sometimes you also get +4096 or -4096. That is an artefact of the ADC that, if saturates, return zero and the reading appears as a 12-bit integer number.

 

We are then ready to make a map of the magnetic field provided by a small magnet. The setup can be made as follows: just use a piece of scotch to firmly attach the sensor on a piece of graph paper, as seen in the picture below.

 

fig1.pngWith the sensor in this position, the X-axis is perpendicular to the paper, the Y-axis  points toward left and the Z-axis points to the top of the figure.


The readings made on each channel must be converted to the proper units (G) before being used and to do that we need to know the calibration constants, i.e. the numbers C such that the value V of the field in a given direction can be obtained as C*R where R is the reading. Those constants depend on the gain of the device that can be adjusted using the sensor's configuration register. The default value of the gain is such that the maximum value of the reading (2048) is attained when the device senses a magnetic field of 1.3 G (see pag. 13 of the datasheet).


In order to get the value of the magnetic field in a given direction returning R as value, we then must multiply R by 1.3/2048.

 

Our purpose is to measure the magnetic field of the earth at the beginning of the run, keeping the magnets far from the sensor. Then, we move the magnet for which we want to map the field closer to the sensor and write down the values read for each position w.r.t. the sensor. From each value we subtract the corresponding value when the magnet is far from the device, to remove the contribution of the earth's magnetic field. Let's then look at the following sketch.

void loop() {  
  if (!(avgDone)) {
    for (int i = 0; i < 100; i++) {
      for (int j = 0; j < 3; j++) {
        b[j] += B(j);
      }
    }
    for (int j = 0; j < 3; j++) {
      b[j] /= 100.;
      Serial.print("B[");
      Serial.print(j);
      Serial.print("] = ");
      Serial.println(b[j]);
    }
    Serial.println("====================================");
    avgDone = true;
  }
  double Bx[3];
  for (int i = 0; i < 3; i++) {
    Bx[i] = B(i) - b[i];
  }
  for (int j = 0; j < 3; j++) {
    Serial.print("B[");
    Serial.print(j);
    Serial.print("] = ");
    Serial.println(Bx[j]);
  }  
  delay(5000);
}

 

avgDone is a boolean variable set as false in the setup() method, such that, as soon as the system is powered on, it takes 100 measurements of the three components of the field by means of the B(i)method that returns the i-th component of the field. To take the average, we store the sum of the readings in a three components array b[]. At the end of the loop the components of such an array are divided by 100. Each of them, then, contains the reading corresponding to the average magnetic field of the earth.

 

The B(i)method is defined as

 

int B(int i) {
  int b[3];

  Wire.beginTransmission(ADDRESS);
  Wire.write(0x03); //select register 3, X MSB register
  Wire.endTransmission();
 
 //Read data from each axis, 2 registers per axis
  Wire.requestFrom(ADDRESS, 6);
  if(Wire.available()) {
    b[0] = Wire.read() * 256;
    b[0] += Wire.read(); 
    b[2] = Wire.read() * 256;
    b[2] += Wire.read(); 
    b[1] = Wire.read() * 256;
    b[1] += Wire.read(); 
  }
  return b[i];
}

 

In fact it can be made more efficient: this way it always reads both the three components, then returns one of them. Since performance is not an issue we can keep the code as it is: keep it simple is always a good rule.

 

The average values measured by us were (-284, -543, -17). In order to check that these values are correct values we compute their values in G using calibration constants. Multiplying each of them by 1.3/2048 we obtain (-0.180, -0.345, -0.011). The magnitude of such a vector is given by the Pythagoras' theorem as the square root of the sum of the squares of the components, i.e. the square root of 0.1802+0.3452+0.0112= 0.151546 (we can just ignore the signs since the squares are always positive), whose root is 0.39 G, not far from the expected value of about 0.5 G (in fact the earth's magnetic field ranges from about 0.3 to 0.6 G, depending on the location).

 

Once the average has been taken and shown on the Serial Monitor, we start taking measurements of the magnetic field every 5 s, so we have the time to move the magnet and write down the values.

DSC_0578.JPG

 

The picture shows how we proceeded: the metal ring you can see close to the sensor is a small neodymium magnet. The position of the magnet w.r.t. sensor can be obtained from the graph paper. For each position we read the three values of the field as provided by the sensor and write them down on the paper in the position occupied by the magnet.

 

In this way we can obtain a complete map of the field, measuring the values at different positions.DSC_0580.JPG Remember that the values provided by our sketch are now the readings in excess w.r.t. the earth's magnetic field, so we can get the magnetic field of the magnet directly from them

 

The picture on the right shows such a map. We reported the values read for the earth's magnetic field and the reference frame (that in fact contains a mistake: the axis pointing up is in fact the z axis).

 

Taking the values on the top right (57, 1, -19) we can see that the magnetic field provided by that small magnet has almost no component along y, so it points below and toward the reader.

 

The real values of the magnetic field are (0.036, 0.000, -0.012) G (just multiply the readings by 1.3/2048). The magnitude of the vector is then 0.038 G.

 

Moving the magnet closer its magnetic field increases. For example, when the magnet is in the bottom left position the readings are (833, 703, -1254) corresponding to (0.529, 0.446, 0.796) G, and the strength of the field is 1.05 G.

 

This kind of magnets provides a field that decreases strongly with the distance, as you can see from the values measured by our sensor.

 

Using a similar technique you can measure the field generated by any magnet, either by moving the magnet or the sensor in space. You can then compute a complete map of the field or of the field strength.

This time I'm going to introduce the usage of a kind of magnetic sensors called digital magnetoresistive sensors. Magnetoresistive sensors are based on a property of several materials called magnetoresistance, consisting in the variation of their electrical resistivity when placed in a magnetic field. In practice, the current flow through a magnetoresistive wire depends on the strength and on the orientation of an external magnetic field. The adjective "digital" in the name of these sensors refers to the fact that they provide just two states: either they are sensing a magnetic field or they are not. In other words, they are not able to provide a precise and accurate measurement of the strength of any external magnetic field: they only react to magnetic fields stronger than a given threshold, providing a sort of signal that can be, in some cases, somewhat proportional to its strength. In most cases these devices are sensitive to the magnetic field only if it is oriented in a given direction (typically parallel to one of the sides of their package).

The Honeywell 2SS52MHoneywell 2SS52M is a device that belongs to the class of digital magnetoresistive sensors. It comes in a compact package with three pins (see picture): two of which are used to bias it (GND and Vcc), 2ss52m-imagewhile the third one is its "output".  The device is sensitive to magnetic fields oriented such that they are parallel to the longest side of its package (the white arrow in the figure). Being the sensor omnipolar, the orientation of the magnetic field doesn't matter: either south-north or north-south alignment triggers the device.

When the external magnetic field in the given direction is strong enough, the device works as a sink for the current. There are, in fact, two kinds of sensors: those whose output mode is source and those whose output mode is sink. A source is a point from which current flows from the device; a sink, on the contrary, is a point to which current flows. In order to operate a sink sensor, you need an external power supply to be connected to the sink through a resistor. If no magnetic field is detected, the sink works as an open switch and current does not flow from the power supply to the device. When a magnetic field is measured, instead, the sink acts as a closed switch and some current flows from the external power supply to the device.

According to the Ohm's Law, when a current I flows through a resistor R, the voltage drop across it is V=RI. In order to tell if the device is sensing a magnetic field, it is enough to measure V: if it is zero, no external magnetic field is present (apart, of course, the earth's magnetic field); if a strong enough magnetic field is placed close to the sensor you should be able to measure a voltage V.

schematic_bb

We can use an Arduino UnoArduino Uno board to build a device that is able to tell you when a magnet is close to a given position(see the schematic above The  2SS52M2SS52M can be operated with a wide variety of input voltages(from 3.8 V up to 30 V Connecting its leftmost pin to the Arduino ground(black wire and its rightmost pin to the Arduino 5V pin(red wire is enough to make it working Then we can use the Arduino 3.3V pin as an external power supply connecting this pin to the central pin of the sensor through a resistor chain will make some current flowing from Arduino to the device when a magnetic field is sensed It is important to limit such a current that must not exceed the maximum current that the Arduino board can provide through its pins 200 mA We must then connect the  2SS52M2SS52M central pin to the 3.3V Arduino pin by means of a resistor of at least

R = V/I = 3.3/0.2=16.5 Ω

Of course, since we do not need a large current flowing, using much higher values is recommended: this way the current flowing through the resistor will be much less, reducing power consumption and heating. For example, using a 2 kΩ resistor will cause a current of I = V/R = 3.3/2000=1.65 mA from Arduino to the sensor. On the other hand, the voltage drop across the resistor will still be 3.3V, independently of the current.

In the schema shown above we illustrate a possible alternative: the resistor through which the current flows from the Arduino to the sink is made by the series of two resistors. We then measure the voltage drop across one of them (we then expect that we are going to read 3.3/2=1.65 V). There is no special reason to do that, but to show how a voltage divider works!

The tricky part comes when trying to measure the voltage drop across the resistor. Voltages can be measured using the Arduino analog pins A0-A5. They measure a voltage up to 5 V with respect to the Arduino ground. However, the voltage drop across the resistor cannot be measured relative to it. Let's try to understand why. In the figure below you can see a schematic of our circuit.

2ss52mVwrong.png

The magnetic sensor is represented as a box connected to the ground and the 5V pin of the Arduino board. The sink is in fact a transistor that conducts when a magnet is close to the sensor and does not if the magnet is far from it. It is connected through a chain of resistors to the 3.3V Arduino pin. If you try to measure the voltage between the ground and the resistor lead connected to the Arduino pin you always see 3.3 V, irrespective of the presence of a magnet close to the sensor, since such a potential is always there with respect to ground.

If, however, you measure the voltage drop across the resistor, i.e. as shown in the picture below,

2ss52mVright.pngyou would see 0 V if no current is drawn from the 3.3 V pin to the sink, while you can measure 3.3 V in the opposite case. We must then measure the voltage difference between the leads of the resistors. To do that we can measure the potentials V0 and V1 at the two leads of the resistor with respect to the Arduino ground and obtain the voltage drop as V=V1-V0. We can, then, measure the voltage V0 connecting one lead of the resistor to the Arduino A0 pin (blue wire), and the voltage V1 connecting the other resistor lead to the Arduino A1 pin (orange wire).

magnets

When no current flows through the resistor the difference between the reading of A1 and A0 will be zero (or very small, because of possible fluctuations in the measurement). If the difference exceeds a given threshold, then, a current is flowing into the resistor, then an external magnetic field has been detected by the sensor.

This device can be used as a proximity sensor Fix the  2SS52M2SS52M to something and a small magnet(see e.g the small neodymium magnets in the picture to something else when the magnet is close enough to the sensor it gives you a signal In the Arduino sketch you can tell when the magnetic field is sensed waiting for a voltage drop different from zero using a function like this

void waitUntilSensed() {
  int j = 0;
  int thr = 100 + analogRead(A0); // get the "zero" level
  while (j < thr) {
    j = analogRead(A1); // read the other lead potential
  } 
  return;
}

When entering into the function, the first operation is to measure the voltage at A0, that is expected to be the same of A1, using the analogRead() function We then just continuously read the A1 pin until its value exceeds those of A0 plus 100 (or any other large enough value). The function returns as soon as a magnetic field is detected.

With two such devices you can build a system with which you can measure the speed of a cart or of a falling object Fix a small magnet to the cart and two  2SS52M2SS52M sensors on a rail at a reasonable small distance x(e.g 5 cm with respect to each other

With the Arduino connected to the sensors you can measure the times of passage of the cart in front of the first and the second sensor: t0 and t1. The time needed to the cart to travel for the corresponding distance x is t=t1-t0, then its average speed is v=x/t. Look at the following sketch.

// the sensor reading for which we can consider it as triggered
#define THRESHOLD 100
// the distance between sensors (in m)
#define S10 0.05

void sense(int pin, long int &t, long int &duration, float &resolution) {
/* This method return the time at which a magnetic field was sensed by a device read on pin*/
  int j = 0;
  int thr = THRESHOLD + analogRead(A0); // get the "zero" level  
  while (j < thr) {
    j = analogRead(pin);
  }
  // get the time at which the signal starts rising
  long int lead = micros();
  while (j > thr) {
    j = analogRead(pin);
  }
  // get the time at which the signal fall down
  long int trail = micros();
  t = (trail + lead)/2;
  duration = trail - lead;
  resolution = duration/sqrt(12);
} 

void loop() {
  // measures the times of passage
  int t0, w0, sigma0;
  int t1, w1, sigma1; 
  sense(A1, t0, w0, sigma0);
  sense(A2, t1, w1, sigma1);
  double v = S10/(t1-t0);
  ...
}

 

The sense() method waits until a non-zero voltage is provided from the sensor connected to the given pin. The micros() function returns the number of microseconds elapsed since the beginning of the sketch. Then, the sense() method provides a measurement of the time at which a magnet passed in front of the corresponding sensor, the duration of the pulse and its error (given by the statistical theory as the width of the pulse divided by the square root of 12). The ampersand in front of the parameters in the function tells the compiler to pass the address of the memory to the function and not its value, such that the readings are all passed to the variables used in the loop() function.

In fact we measure the time at which the voltage between A0 and another pin becomes different from zero, then we wait until it becomes zero again (in the second while loop). Measuring the time at which the signal starts to be non zero and the one at which it returns to zero, we can assume that the magnet passed in front of the sensor at a time given by the average of the two times. The duration of the passage is given by the difference of the two times.

Measuring the times of passage in front of two sensors we are then able to tell the speed of the cart as the ration between the distance traveled S0 and the time of travle t1-t0. With three or more sensors, you can then measure the speed at various positions of an object to study its motion. With such a system you can do many physics experiments, in fact. The cost of such a system can be below 40 EUR, while a professional system to make the same experiments with a comparable precision may cost from hundreds to even thousands of euros!

According to my very preliminary tests, I was able to trigger the device using a small neodymium magnet (4 mm diameter) when it was at about 2-3 cm distance from the sensor. My plan is to make a systematic measurement campaign that will be reported on my free e-book on scientific uses of Arduino boards. The e-book is in preparation and there is a very preliminary version at http://www.roma1.infn.it/people/organtini/publications/scientificArduino.pdf.

In my previous post on the subject I showed how to configure the Arduino Ethernet shield, in such a way you can save a lot of memory. Now it's time to use the shield for something useful.

Arduino is mostly used to take data from sensors and to drive actuators to perform actions (such as motors, servos, relays, etc.). In this post we are going to use Arduino to collect some data (e.g. temperatures) and store them on a computer disk for subsequent analysis. As a physics teacher, in fact, I am interested in using Arduino as a laboratory tool and in physics you always collect data from experiments to be analysed offline.

Then, suppose you want to perform the following calorimetry experiment: take a resistor and wrap it in a waterproof material; then connect its leads to a voltage generator and make current flow through it. If R is the resistance of the device and V the voltage across its leads, the Ohm's Law states that the current flowing is I=V/R. The resistor dissipates heat, because of the Joule's effect, as W=RI2, where W is the amount of energy per unit time. If you plunge the resistor into water, the energy released by the resistor causes the heating of the water and you expect that the temperature of the water raises linearly with time.

You can perform this experiment using an LM35 connected to an Arduino to measure the water temperature versus time (the sensor leads must be made waterproof, of course, e.g. using some heat-shrink tubing). An Ethernet shield can then be used to send data to a computer.

Let' start looking at the Arduino sketch, shown below.

#include <Ethernet.h>
#include <SPI.h>

#define PORT 5000
#define LM35PIN A0

byte mac[] = {0x5e, 0xa4, 0x18, 0xf0, 0x8a, 0xf6};
IPAddress arduinoIP(192, 168, 1, 67);
IPAddress dnsIP(192, 168, 1, 1);
IPAddress gatewayIP(192, 168, 1, 1);
IPAddress subnetIP(255, 255, 255, 0);

EthernetServer server(PORT);
boolean notYetConnected;

void setup() {
  Ethernet.begin(mac, arduinoIP, dnsIP, gatewayIP, subnetIP);
  notYetConnected = true;
}

void loop() {
    int i = 0;
    EthernetClient client = server.available();
    if (client) {
      if (notYetConnected) {
        client.println("Welcome!");
        notYetConnected = false;
      }
      if (client.available()) {
        unsigned long now = millis();
        int lm35 = analogRead(LM35PIN);
        now += millis();
        double T = 5000.*lm35/10240.;
        server.print(0.5*now);
        server.print(" ");
        server.println(T);
      }
    }
} 

The first include directives are needed to use the Ethernet shield. Then we define two symbols: PORT is used to send data over the Internet, LM35PIN represents the Arduino pin to which the LM35 sensor is connected.

Besides the addresses used to configure the Ethernet shield (see my previous post), a number of data members are defined: in particular, the EthernetServer object called server is instantiated, listening on port PORT. This creates an object in the Arduino memory that connects to the Internet and waits for signals on the given port, represented as an integer (5000 in the example).

The setup() method just initialise variables and configure the Ethernet shield. The most interesting part is in the loop() method. Here we instantiate (create) an object called client of class EthernetClient. Such an object is returned by the server object that continuously polls the port to which is connected. If no client is connected, the server returns NULL. Then, as soon as client is found to be not NULL, and available for communication, we can send and receive data to/from it.

Before sending data we must get them: first of all we obtain the current time as the number of milliseconds elapsed since the beginning of the execution of the sketch (we don't care about the absolute time of the event). This time is returned by the function millis() and is represented as an unsigned long integer, i.e. a binary code of 32 bits. With 32 bits, the highest number that can be represented is 232-1= 4294967295. Dividing this number by 86400 (the number of seconds in a day) and by 1000 we get about 50: this is the number of days during which the millis() can work without reaching the overflow condition. In other words, there is plenty of time to perform our experiment.

Then we get the reading from the LM35 sensor using analogRead and measure the time again. Averaging the last time with the one previously measured provides a better estimate of the time of reading. Note that, in between, we just read raw data, in such a way we minimise the time spent in data acquisition and obtain the time with as much precision as possible. Computing the temperature in degrees is made after getting the time: the analog pin reading is a 10 bits binary number: its highest value (1024) corresponds to an input of 5 V, i.e. 5000 mV. The actual temperature, in Celsius, can be obtained reading the output voltage of the sensor divided by 10.

To transmit data to a remote client, it's enough to call the print method of the server object. We then print the time reading, a blank and the actual temperature. Without any delay in the loop(), the sketch will read temperatures at a rate of one measurement every few milliseconds (quite fast, indeed).

In order to collect those data on a computer you need an Internet client that connects to the Arduino port 5000, writes some data on that port to announce it (knock, knock) and waits for data. An example of such a program in C is below.

#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>

#define PORT 5000
#define ADDRESS "192.168.1.67"

int main() {
  int len;
  int i;

  /* create socket */
  int sock = socket(AF_INET , SOCK_STREAM , 0);
  if (sock <= 0) {
    printf("Can't create socket. Error");
    return -1;
  }

  struct sockaddr_in server;
  server.sin_addr.s_addr = inet_addr(ADDRESS);
  server.sin_family = AF_INET;
  server.sin_port = htons(PORT);

  /* connect */
  if (connect(sock , (struct sockaddr *)&server , sizeof(server)) < 0) {
    printf("can't connect to the server. Error");
    return -1;
  }

  printf("CONNECTED: hit return to start DAQ\n");

  char message[255];
  scanf("%s", message);
  send(sock, message, strlen(message), 0);

  /* read */
  while (1) {
    unsigned char c;
    recv(sock, &c, sizeof(unsigned char), 0);
    printf("%c", c);
  }

  return 0;
}

Briefly, we first create a so-called socket to make a connection between the client and the server (the Arduino). We must connect the socket to the same port to which the server is listening at. Once connected, with scanf we just read a string from the keyboard and send it to the server. This way the server answer and data acquisition starts. Data sent from the server are read with the recv statement (one character at a time). In the above example the reading loop lasts forever and just print characters on screen. You can, of course, write data on a file until some event happens (e.g. key pressed, maximum number of data received, etc.).

On the Farnell Element14 site you can find all the products mentioned above (just follow links) and many others, with which you can realise much more experiments using a similar technique.

The content of this post can be found in my blog, too, and will be available as a chapter in a free publication of mine: Arduino Scientific Programming.

This is the first of a series of posts devoted to the (clever) usage of Arduino, with particular emphasis to scientific tasks. The content of these posts will also be available on my free publications on using Arduino for scientific purposes: Scientific Arduino Programming.

With respect to other similar posts, mine will not only show how to do something using Arduino: as a physics teacher I will explain why you are required to do that. In order to use any tool at its best, it is important to understand how it works behind the scenes.

As a physicist I often do measurements. Some of them can be done using an Arduino board equipped with some sensor. In fact, the advent of Arduino boards was a great advantage, mostly for those in need of cheap data acquisition systems for scientific purposes, like teachers in high schools or graduate students.

Most scientists do not take measurements, analyse them and show results in one single application: taking data can be a hard job that can keep your CPU very busy. Hence, in most cases, they just take as much data as they can out of the sensors and barely copy them as such on some mass storage device. Those data are then analysed offline using the best available tools that not necessarily coincide with the best tools for data acquisition. Presentation is a further step: once data have been analysed, the relevant results must be presented in the proper way and that job can be done with an even different technology.

In this post we show how to use Arduino to take data from some sensor and store them for further analysis. There are at least two very effective solutions: storing data onto files on SD cards or transfer data over the Internet to a remote location. The first solution is fairly simple, but data cannot be accessed during data acquisition; if the data taking persists for a long time you may prefer adopting the second solution.

Let's then assume we are going to use an Arduino board to get the values of few sensors during an experiment that lasts for a relatively long time. Moreover, imagine that the equipment used to take data needs to be kept in conditions such that it is difficult to have access to it (e.g. a dark room, or a climatic chamber). The only solution to get the data out of the system in almost real time is to use an Ethernet shield, i.e. an Arduino shield capable to connect to the Internet and exchange data over the connection.

In order for any device to connect to the Internet, the device must acquire a unique identity on the network: the IP address. The IP address (IP stands for Internet Protocol) of a device is a unique identifier composed, in the IPv4 version of the standard (the most commonly used), of four bytes, each of which can have a value between 0 and 255. Few of them are reserved internationally and, in particular, subnetworks whose first two bytes are 192 and 168 are non-routable, i.e. packets sent over such a network cannot go beyond an Internet switch. In other words they can only reach those devices on the same physical network. That's why devices in a home network usually have IP addresses like 192.168.x.y. The IP address of a device can be statically or dynamically assigned to it. In the first case, the device administrator tells the device its IP address: in this case the address is going to remain the same forever. A device not having a static IP address can ask for an IP address to any computer on the same network running as a DHCP server (Dynamic Host Configuration Protocol). Depending on the configuration of the server, available IP addresses can be assigned to the device randomly or based on the device identity.

Every physical device, in fact, brings a unique MAC (Media Access Control) address: a set of six bytes, usually expressed in the hexadecimal notation, as 5e:a4:18:f0:8a:f6. The MAC address is broadcasted over the network so that a DHCP server can choose if the request must be ignored, served using a randomly chosen address or with a fixed address.

Having an IP address in not enough for a device to communicate with other devices: data packets must reach a special device, called the gateway, that knows how to send data to the recipient. The gateway, too, must be assigned an IP address and its address must be known to the devices aiming to communicate with others.

IP addresses can be associated to strings composed of a host name and a domain name. All devices on the same network shares the same domain name, while host names are unique. A DNS (Domain Name System) is a device able to associate the IP address of any other device to its host name.

The last piece of information needed to setup a network device is the subnet mask. This is a rather technical element, but we can think of it as a way to identify the range of addresses a device can reach directly DSC_0434.JPGthrough a gateway. It is usually in the form of an IP address (but it is not, it is a mask) and often equal to 255.255.255.0, meaning that all the devices on the same network share the first three bytes of their IP address.

We now have all the ingredients to connect our Arduino to the network: first of all plug the Ethernet shield to the Arduino board (see the picture), then connect the shield to your router using an RJ45 cable. In order to configure the device you must know the MAC address of your shield (you can find it printed on the box or you can just invent it, provided it is unique in your network). Consider now the following excerpt of Arduino sketch:

#include <Ethernet.h>
#include <SPI.h>

byte macAddr[] = {0x5e, 0xa4, 0x18, 0xf0, 0x8a, 0xf6};
IPAddress arduinoIP(192, 168, 1, 67);
IPAddress dnsIP(192, 168, 1, 254);
IPAddress gatewayIP(192, 168, 1, 254);
IPAddress subnetIP(255, 255, 255, 0);

void setup() {
  Ethernet.begin(mac, arduinoIP, dnsIP, gatewayIP, subnetIP);
}

Including Ethernet.h and SPI.h is mandatory: that files contain the definition of the classes used in the sketch. The MAC address is defined as an array of bytes, each of which is represented as a pair of hexadecimal digits (thanks to the 0x preceding each number). The IP addresses of the shield, the DNS and the gateway is given as an object of class IPAddress, as well as the subnet mask (that is not an address, but has the same structure). The object constructor (called after the declaration, as in IPAddress arduinoIP(192, 168, 1, 67);) takes four arguments that represent the four bytes of the address. Our Arduino will then acquire a fixed IP address 192.168.1.67, in a network whose gateway's address is 192.168.1.254; the gateway works also as the DNS in this case, while the subnetwork is restricted to those devices having an IP address like 192.168.1.x.

The Ethernet.begin(mac, arduinoIP, dnsIP, gatewayIP, subnetIP) call does the job: it configures the Ethernet shield as above (and, of course, it does that in the setup() method).

In many tutorials you can easily find a much simpler configuration, that reads as

#include <Ethernet.h>
#include <SPI.h>

byte mac[] = {0x5e, 0xa4, 0x18, 0xf0, 0x8a, 0xf6};

void setup() {
  Ethernet.begin(mac);
}

In this case the Ethernet shield acquire a dynamic IP address from a DHCP server on the network. In fact the begin() method of the Ethernet class exists in many variants (it is said to be polymorphic). To many novices the last sketch may appear much more convenient: it's simpler and shorter and does not require the knowledge of too many parameters. However, attention must be paid to the size of the memory. It turns out that the length of the source code has mostly nothing to do with the size of the sketch in the Arduino memory.

This happens because what is stored in the Arduino memory is not the sketch as you can see in the editor, but the sketch in machine language. Microprocessors work using electrical signals representing data and instructions. Because an electrical device can be easily found in two states (e.g. on/off), information (data and instructions) is represented as binary strings. A program for a microprocessor is then a long sequence of bits 0 and 1, not a flow of characters. The characters you write in the editor are translated into corresponding sequences of bits by the compiler (automatically invoked before uploading the sketch or when you click on the Verify button of the Arduino IDE). It is this long sequence of bits that is uploaded on the Arduino memory, not your sketch.

It happens that, in order for the shield to ask for an IP address to a DHCP server, the number of operations to perform is much larger with respect to those needed to assign manually all the parameters. As a result, the compiled program in the two cases is very different in size: the first sketch takes 2634 bytes in memory, once added an empty loop() method; the latter takes 10424 bytes! It's about a factor 4 more space!

The memory space of an Arduino is precious, since it is not so large: as a result you may prefer the apparently longer sketch of the first example to the second one. This is a very clear demonstration that knowing what you are doing is extremely important! Don't just copy and paste computer code! Always try to understand it!

Now we own an Internet connected Arduino board. We are going to take data with it and send them to some remote storage for further analysis. In the next post we will show how to do that efficiently. Stay tuned...

Filter Blog

By date: By tag: