Friday, 26 May 2017

Experimenting with the LM35 Temp sensor

A few days ago, my breadboard and power supply arrived, so I have already been doing some testing with the LM35 sensor I had rescued.  You may remember I mentioned it was on a circuit board, and the output from the sensor was being pushed through an op-amp chip.  I have no idea what that chip was doing to the signal, and hooking the output from that up to my multimeter was giving some very inconclusive readings.  So I figured the safest bet was to remove the LM35 sensor from the board and use it on its own - at least then I have the specs and can understand what it is supposed to be doing.

Now that I have my Arduino as well (though no display as yet), last night I put together a little test sketch just to see what the LM35 on its own provides.

The sketch is a simple loop that takes a reading, converts it to temp, checks against min and max values, and then prints the results out to the serial monitor.  However - my first attempt resulted in 20.0C over and over again.  I put in some extra print lines to troubleshoout the raw value coming from the A0 pin, and found it to be showing slightly different values each time as I would have expected, but after a little research I found the problem... I was using the simpler map() function to translate my analog value between 0 and 1023 into a temperature between 0 and 110C, rather than messy mathematics.

          float celsius = map(pinVal,0,1023,0,110);

It turns out that the map() function ONLY produces INT values, so the small variations I was seeing on the analog pin, were not big enough to generate a whole degree of difference.

I reverted back to the mathematical approach, and started to get much better results, showing different readings varying by tenths of a degree (though adjacent readings separated by just 5 seconds could sometimes be half a degree different - so I question the accuracy of the sensor when in the open - perhaps it needs to be in an enclosure to protect it from drafts, etc.

          float celsius = (pinVal/1024.0) * 110.0;

Now, with a steady stream of data arriving through the serial console and rapidly scrolling off the top, I thought it might be nice to see how long the sketch has been running, and initially just included the millis count (oh - by the way, I have discovered that the millis count doesn't reset every 9 hours or so as I originally thought - apparently that figure was a throwback to an old version from around 2007!!  The current millis counter will run a little short of 50 DAYS before wrapping around to 0 again).

Then I figured it would be nice to show the elapsed time in an hh:mm:ss format, so set about converting it, like this...
Take the elapsed millis and divide it by the number of millis in an hour, to get an (int) hrs value
Subtract (hrs times the number of millis in an hour) from elapsed millis
Take the modified elapsed millis and divide it by the number of millis in a minute,
Subtract (mins times the number of millis in a minute) from elapsed millis
Take modified elapsed millis and divide by 1000 to get an (int) secs value
Finally, display each of the elements on the console.
I found that there is seemingly no easy way in Arduino code to do something like
   print(hrs + ":" + min + ":" + secs)
so the simplest solution looks like being a whole bunch of Serial.print(); statements in my sketch.  It ain't pretty - but at least it is easy to follow.

So finally, here is my little test sketch...

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
int LM35Out = 0;           // Analogue pin 0 
float min = 100;
float max = 0;
int counter = 0;

void setup() {
  // put your setup code here, to run once:

     Serial.begin(9600);
     analogReference(INTERNAL);    // use 1.1V reference to get greater granularity on analogRead
}

void loop() {
  // Get reading and increment counter
     int pinVal = analogRead(LM35Out);
     counter++;

  // Convert analog value to temp (don't use MAP as it calculates int values only)      
     // float celsius = map(pinVal,0,1023,0,110);
     float celsius = (pinVal/1024.0) * 110.0;

  // Check and set max and min values
     if (celsius > max) max=celsius;
     if (celsius < min) min=celsius;

  // Keep note of elapsed time
     unsigned long elapsed = millis();

  // Convert elapsed millis into hh:mm:ss
     int hrs = elapsed/3600000;
     elapsed = elapsed - (hrs*3600000);
     int mins = elapsed/60000;
     elapsed = elapsed - (mins*60000);
     int secs = elapsed/1000;

  // Print results to serial monitor
     Serial.print("Reading: ");
     Serial.println(counter);
     
     Serial.print("Elapsed time = ");
     if(hrs<10) Serial.print("0");
     Serial.print(hrs);
     Serial.print(":");
     if(mins<10) Serial.print("0");
     Serial.print(mins);
     Serial.print(":");
     if(secs<10) Serial.print("0");
     Serial.println(secs);
     
     Serial.print("Temp is now ");
     Serial.print(celsius,1);
     Serial.println("C");
     
     Serial.print("Max was ");
     Serial.print(max,1);
     Serial.println("C");
     
     Serial.print("Min was ");
     Serial.print(min,1);
     Serial.println("C");
     Serial.println();
     
  // Wait 5 seconds between readings   
     delay(5000);
}
...and the output in the serial monitor window.


Thursday, 25 May 2017

My Arduino has arrived - finally.

I should probably have expected no less, but the Arduino arrived today - on the very last day in the expected delivery period, over a month since I ordered it.  Oh well, at least I have it now.  The package was actually well protected, with the board (and a bonus set of header pins I wasn't expecting) in a static-proof bag, and then that in turn was vacuum sealed in an outer bag for delivery.

My excitement at receiving the package was dulled a little when I opened it and actually held the board in my excited clammy hands... it has a USB MICRO socket on it - not the USB MINI that I was expecting... DAMN!  Luckily, after a little digging around, I found that I had a USB lead probably from an old phone (think it may have been a Samsung Galaxy... but not sure) that fit the bill.

My next observation was that the extra 3 rows of connection points described in the ebay listing (Serial Comms, I2C @ 5V, and an extra set of 3.3V/GND outputs)... was actually only 2 rows - and which was the one missing? You guessed it - the one for 5V I2C connection that I was hoping to use for my LCD unit :-( 
However - instead of the extra 3.3V/GND connections, they have provided a set configured for I2C connections at 3.3V, so I'll have to check if that can drive the LCD (and backlight) when the LCD arrives (probably next week if that goes to the last delivery day as well...)

So, being a little upset at the time (having fallen foul of the very things I half-expected would happen, back when I placed the order) I let the ebay seller know how disappointed I was - in no uncertain terms!!  Maybe that was a bit churlish of me for something that only cost $4 and was quite obviously a cheap clone anyway, but it was the principle of the thing - I checked with the seller before I ordered; waited 5 weeks for it to arrive; and then found it did not match the description of the article I purchased - or thought I had purchased (vague though they were).  I just needed to vent and didn't really expect a response to my rant, except that I threatened them with neutral fedback.  That got their attention - and to their credit, they responded within an hour and offered me a 30% refund!  Cool - thank you very much - I accept :-)

But - oh dear - that refund will go into my PayPal account, which means I will just have to spend it on something else now (first world problems...)!

So having got over my initial disappointments, and with excitement rising again (while already planning what to spend my extra $1.40 on...), I plugged the board into one of my power supplies (9v@1A).  The ON light came on, and the L light (attched to pin 13) started flashing rapidly - great news... it works - apparently.


My next move was to plug in the USB cable, and upload the simplest sample sketch that would give me a visible indication of success on the board itself, with no other devices plugged in - in other words, the 'Blink' sample code.

USB connected? Check.
Sample sketch opened in IDE? Check.
Arduino UNO selected it Tools | Board? Check.
COM port?  Well, it lists COM1 and COM3 - I know what COM 1 is connected to, so it must be COM3... right? Check!

I clicked on the Upload button - the sketch compiled - and...

"Houston? We have a problem..." (apparently, though Tom Hanks said this in the movie, that's not what the actual crew members of the Apollo 13 said.  I imagine what they said was something more like "What the f@*$ was that?", but I digress...)

No upload - no flashing Tx/Rx lights - and after about 20-30 seconds, a whole string of error messages in the IDE, indicating that it cannot communicate with the board.  This can't be good..?



That's OK, I seem to remember reading something about this (but it was well over a month ago when it was all fresh in my mind!!).  As I said, COM 1 and COM3 were both listed in the IDE Tools | Port menu option, but neither of them vanished when I unplugged the UNO :-/ 
OK - this means that they must both belong to pre-existing devices.
I went to the device manager on my PC to look for an unrecognised device listed in the COM ports, but there weren't any.  BUT - I saw a device labelled as "USB 2.0 - Serial Device" listed under "Other Devices", which had the little exclamation mark on the icon - meaning Windows has no idea what it is, but knows it is there.  I selected the device and tried to do a driver update - letting windows 'Search automatically...', but this did not find a suitable driver.  Next, I tried pointing the search to look at the Drivers folder in the Arduino folder on my PC.  This also failed to find a suitable driver. By now, I am starting to get a bit fed up again...

Then I had one of those "Aha!" slap-my-forehead moments and remembered what it was I had read a month ago...  Dammit - I even wrote about here it in my first post!! The Chinese clone UNO boards do not use the standard licensed USB controller chip.  Instead, they use the CH340 or CH340G chip, which requires a specific driver.  A check of my UNO confirmed it has the ...G variety of chip, and google search listed a few locations where I could download the driver.  This is the one I used - it is for Windows 8 and above and for both x86 and x64 (I am on W10 x64).  NB - you need administrator rights on your PC to install this driver.  After downloading the driver (zip file), I unpacked it into a new folder under the ...Arduino\Drivers folder.

My 'normal' ID on my computer has been specifically configured to NOT have admin rights, so when I pointed the "USB 2.0 - Serial Device" driver update at this specific folder, it still wouldn't work, and I ended up installing the driver manually, by right-clicking on the driver's setup.exe and running it 'as Administrator'.  The setup ran, and the device manager screen updated to show a new device "USB-SERIAL CH340" as COM4.

I went back to the Arduino IDE, looked in Tools | Port, and lo... there was COM4 listed :-)  Deep joy!

I hit the upload button again, the sketch compiled, there was the briefest of flickers on the RX and TX lights (blink and you'd miss it... maybe the sketch should be called DON'T blink?), and then the L light changed from the initial rapid blinking to a more stately 1 second on, 1 second off.

SUCCESS - I have a functioning UNO board, and connection to my PC.  Now I can start to play :-D

Sunday, 21 May 2017

SK001 - in theory... (pt 4)

Hi again,
time for the final part of the puzzle - loop().  This is where all the preparations come together to actually do something.  Then once it has done that something... it does it again... and then guess what?  It does it again.  A bit repetitive - yes?  Well - not necessarily.  Remember those variables we set up at the beginning, and how we said that they were 'global', so were available to all of the functions in the sketch?  Well, that includes from loop to loop of loop().  This means that one time through, we might set a variable to be a certain value, but the next time we go through loop, that value acts as a trigger to make something different happen - like this...


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
str trafficLight;

void setup()
{
     trafficLight = "RED";
}

void loop()
{
     if (trafficLight == "RED") 
     {
          trafficLight = "RED and AMBER";
     }
     else if (trafficLight == "RED and AMBER")
     {
          trafficLight = "GREEN";
     }
     else if (trafficLight == "GREEN")
     {
          trafficLight = "AMBER";
     }
     else
     {
          trafficLight = "RED";
     }
}
Disclaimer - the code above may not actually work if you try and load it into your Arduino - it was intended only to illustrate a point.
The first time we go through loop(), the light is already set to red, and so it is changed to red and amber.  The next time through, red and amber are changed to green.  The next iteration will set it back to amber, and then finally back to red, and so the cycle starts all over again.  Add in some delays, and you have a set of (UK) traffic lights.  So you can see that in this instance, there are four distinct and different operations performed by loop().  However, even these follow a set pattern, and could all be in one large chunk of code inside of a single iteration of loop().

Now bring some real world randomness into the picture, using the Arduino to react to outside influences, such as the temperature.  Each time through the loop, the current temperature is read from a sensor.  If it is greater than the previously stored maximum, then it replaces it - or if it is lower than the previous minimum, it replaces that.  If the light is on, and 10 seconds has passed since it was switched on, then switch it off.  Hey, does all this sound a bit familiar to you?  Yeah - me too... so let's see it in action!

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
void loop() 
{
  // Read analog value (0-1023) on LM35 output connected to A0
  int analogVal=analogRead(LM35out);

  // convert analog value (0-1023) to millivolts (0-1100) to celsius
  //float celsius=(1.1 * analogVal * 100.0) / 1023.0;
  float celsius=map(analogVal,0,1023,0,110);

  // Display "Temp now" with 1 decimal
  lcd.setCursor(13,0);
  if (celsius < 10) 
    lcd.print("0");      // Add leading 0 if < 10C
  lcd.print(celsius,1);

  // if "Temp now" is a new high...
  if (celsius > maxTemp)
  {
    //store current value as new high
    maxTemp=celsius;

    // and change value on display
      lcd.setCursor(13,1);
       if (maxTemp < 10) 
        lcd.print("0");      // Add leading 0 if < 10C
      lcd.print(maxTemp,1);
  }
  
  // if "Temp now" is a new low...
  if (celsius < minTemp)
  {
    //store current value as new low
    minTemp=celsius;

    // and change value on display
      lcd.setCursor(13,2);
      if (minTemp < 10) 
        lcd.print("0");      // Add leading 0 if < 10C
      lcd.print(minTemp,1);
  }

  // Backlight Operation
  // -------------------
  
  // check if backlight is on
  if (lightState == "on")
  {
    // switch backlight off after 10 seconds
    // NB - If you happen to switch light on just before the clock hits max and resets to 0, then light may only stay on for 1 or 2 three second period.
    // But since the millis() count only resets every 9 hours, it is unlikely you will hit just that 10 second period.
    // I could code around it, but can't be bothered to spend the time for something so unlikely.
    if (abs(millis() - lightOnTime) > 10000)  
    {
      // switch backlight off immediately
      // lcd.noBacklight();
      // or fade to off over 1 second
      for (int i=255; i>=0; i--)
      {
        lcd.setBacklight(i);
        delay(4);
      }
      lightState="off";
      lightOnTime=0;
    }
  }

  // check for backlight off, and pushbutton pressed
  if (lightState == "off")
  {
    // check for pushbutton press
    if (digitalRead(lightSwitch) == HIGH)
    {
      // switch backlight on immediately
      // lcd.Backlight();
      // or fade on over 0.5 second
      for (int i=0; i<256; i++)
      {
        lcd.setBacklight(i);
        delay(2);
      }
      lightState = "on";
      lightOnTime = millis();
    }
  }
  
  // Averaging Calcs
  // ---------------
  
  //    increment counter
  fiveMinCount++;
  
  //    accumulate value
  fiveMinAccum = fiveMinAccum + celsius;
  
  //    calculate and display average for current 5 min period
  fiveMinAve = fiveMinAccum / fiveMinCount;
  lcd.setCursor(13,3);
      if (fiveMinAve < 10) 
        lcd.print("0");      // Add leading 0 if < 10C
      lcd.print(fiveMinAve,1);
 
  //  write temp, max, and min to serial console
    Serial.print("Current temp = ");
    Serial.print(celsius,1);
    Serial.println(char(176) + 'C');

    Serial.print("Max temp = ");
    Serial.print(maxTemp,1);
    Serial.println(char(176) + 'C');

    Serial.print("Min temp = ");
    Serial.print(minTemp,1);
    Serial.println(char(176) + 'C');

    
  //    if end of a 5 minute period, then write average temp to serial console, reset and start again.
  if (fiveMinCount >= ((5 * 60 * 1000) / loopPeriod))
  {
    Serial.print("Latest 5 minute period average = ");
    Serial.print(fiveMinAve,1);
    Serial.print(" at ");
    Serial.println(millis());
    fiveMinAccum=0;
    fiveMinCount=0;
    fiveMinAve=0;
  }
  
  // wait a few seconds before retesting
  delay(loopPeriod);

}

WOW - that's a lot of code, but in my defence, a lot of it is comments ;-)  So let's break it down into its component parts.

Line 4 simply reads the signal from the LM35 attached to analog pin 0.  Remember, this will be a voltage somewhere between 0 and 5 volts (hopefully lower than 1.1 volts so it can be evaluated against the INTERNAL reference voltage).  The Arduino translates this into a number between 0 and 1023.

Line 8 takes the possible numbers 0 to 1023, and maps those to 1024 increments between 0 and 110 to match our expected temperature range. The actual incremental point is stored in a floating point variable called celsius.

Lines 11 to 14 basically write the celsius value as a two digit plus 1 decimal value at position 13 on line 0 of the display. If the temperature is lower than 10 degrees, then the code will insert a leading zero, so the decimal point always stays in the same place (yes - I realise I will have problems when it reaches 100C... but given that this is designed to read ambient air temperature in my office, if it ever reaches 100C, then the position of the decimal point is not likely to be of immediate interest to me).

Line 17 checks to see if this is a new maximum temperature.
If it is, then line 20 stores the current temperature as the new maxTemp, and lines 23 to 26 print the value (just like above) on line 1 of the display.  Notice that the maxTemp value is ONLY updated on the display when the value actually changes - this is to eliminate the possibility of a flicker caused by the value being overwritten with same value every single time it goes through the loop.

Lines 30 to 39 do the same again, but this time checking for a new minimum temperature, and writing it to line 2 (if required).

Line 46 checks to see if the backlight on the display has been switched on (zoom forward to line 68 to see how it is switched on, and what happens then...)
If the backlight IS on, then line 52 looks at the millisecond counter value now, and compares it with the millisecond value when the light was switched on.  If the difference is > 10,000 (i.e. 10 seconds), then lines 57 to 63 switch the backlight off again (notice - I used the 'fade' style of code here, that I mentioned way back at the start when I flash the display on and off a few times when I first switch it on).  I also set the backlight status variable to "off", and set the millisecond 'on' time to 0.

In case I hadn't already mentioned it, the millisecond counter just counts milliseconds (wow - really?  Yes - really!) since the sketch started running.  It can keep going for around 9 hours or so, and then rolls back to 0 and starts all over again.  Note my comments about the potential problem if the light was switched on just before the counter rolls back to 0...

Now we come to the code that handles switching the light ON.
Line 68 checks to see if the light is off.
If it is, then line 71 checks to see if the button attached to digital pin 8 has been pressed (connecting it to Vcc or +5v and giving a HIGH value on the pin).
If it has... then lines 76 to 80 fade the light up, line 81 sets the backlight status variable to "on", and then line 82 makes a note of the current millisecond count.

Remember - the backlight status variable, and the stored millisecond count, will both be available to be used in the next iteration of loop(), and the next, and the next, until eventually, 10 seconds will have passed, and then lines 46 to 63 will turn the light off again.

Now we come to another instance of variable values that get changed over a number of passes through loop(), until eventually, a trigger causes an action.  In this case, it is the accumulation of average temperature data which will be re-calculated every few seconds, then eventually written to an external device at the end of each period of 5 minutes, and the values reset and calculations started afresh.  In this case - the 'external device' is simply the serial console, but could eventually be an SD card for data logging, or plotting on a graph, etc.

In line 90, a counter is incremented.  This was initially set to 0 right back at the beginning of the sketch, and is used to count each iteration through loop().

Line 93 adds the current temperature (celsius) to an accumulator, which was also set to zero at the start.

Line 96 recalculates the average temperature, by dividing the cumulative temperature, by the number of readings (the count of loop iterations), and lines 97 to 100 print the averaged value to line 3 of the display in the now familiar fashion.

Lines 103 to 113 simply replicate all the LCD values displayed above (currently with the exception of the averaged value, but I will include that one too), on to the serial console.  This functionality was added a) to demonstrate how 'debugging' information can be sent to the console to help monitor the progress of the sketch's logic and variable values, and b) just in case my display is delayed and I want to actually try to run this sketch before it arrives!

Line 117 takes the number of milliseconds in a 5 minute period, and divides that by the 'delay' period per loop.  This gives the number of iterations through loop() that can be made in 5 minutes.  This number is compared with the actual loop counter (see line 90), and once the 5 minute period is exceeded... (it is unlikely to be accurately hit, due to the variable cost in milliseconds of all the operations within each iteration of loop() which can vary based on the logic and cumulative values) ...then lines 119 to 122 write the average value to the serial console (substitute other external device here when data logging or plotting function is required), and lines 123 to 125 reset all the variables used during the 5 minute average calculations, ready for another go.

Finally, line 129 sets a short pause delay at the end of loop(), before the whole process starts over again, and a new temperature reading is taken.

PHEW!!  If you are still here - congratulations - you have survived my first and pretty lengthy description of a (still theoretical) real world application of the Arduino.  Now that is what I call a starter sample - but maybe not for the absolute starter!!  Even if you haven't fully understood some of the syntax, or the logic, I hope it has given you some insight, and whetted your appetite.

Now, as soon as my Arduino arrives, and I can spend the time putting this circuit into operation, I will post "SK001 - in practice", which will include any changes I have to make to my code in order to get it to actually work, as opposed to this theoretical version.

Tuesday, 16 May 2017

SK001 - in theory... (pt 3)

Now we come to the setup function.  This is where we do all the one-time initialisation, getting everything ready to go...


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
void setup()
{

  Serial.begin(9600);         // initialize serial comms at 9600 baud
  lcd.init();                 // initialise the display

  // flash backlight for 1 second on startup
  for (int i=1; i <= 4; i++)
  {
    lcd.backlight();        // may be an alias for lcd.setBacklight(255);  Try adjusting brightness with values 1-254.
    delay(125);
    lcd.noBacklight();      // may be an alias for lcd.setBacklight(0);
    delay(125);
  }

  // turn off cursor options
  lcd.cursor_off();
  lcd.blink_off();
  

  // setup backlight pushbutton
  lightState="off";
  pinMode(lightSwitch, INPUT);
  lightOnTime = 0;

  // write constants to display (Coordinates = col,row)
  lcd.setCursor(2,0);
  lcd.print("Temp now = --.-");
  lcd.print((char)223);
  lcd.print("C");
  lcd.setCursor(2,1);
  lcd.print("Max temp = --.-");
  lcd.print((char)223);
  lcd.print("C");
  lcd.setCursor(2,2);
  lcd.print("Min temp = --.-");
  lcd.print((char)223);
  lcd.print("C");
  lcd.setCursor(2,3);
  lcd.print("5min ave = --.-");
  lcd.print((char)223);
  lcd.print("C");

  // set Analog Input to use internal 1.1V reference rather than VCC 5V
  // this reduces upper limit to 110C, but gives greater resolution of nearly 0.1C vs 0.5C.
  analogReference(INTERNAL);
 
}

Line 4 sets up the ability to have serial communications with the controlling PC.  It isn't really required in this specific sketch, but it is generally a good idea to have this ready for use for troubleshooting  when things go wrong (as they will).  I'll cover troubleshooting in a later post, but in a nutshell, serial communications allow the Arduino to talk to the PC across the USB cable.  This can be as simple as writing the value of a variable out to the serial console (visible in the Arduino programming IDE), or writing data to the hard disk (or reading it from the hard disk), amongst other things.

Line 5 initialises the lcd.

Line 8 through 14 is what is known as a 'for...next loop'.
Line 8 sets up the loop control.  It creates an integer variable called 'i', and starts by setting it to 1. The next part of the loop control sets the ending condition for how many times the loop is run - all the while i is less than or equal to 4, then the loop will keep running.  Finally, at the end of each loop, the value in i is incremented by 1 (that is what i++ does).
The loop itself is all the instructions between the curly brackets.
Line 10 calls a function called backlight from the external code library, via the object called lcd. This switches the lcd backlight on.  Notice in the comment, it appears that you can actually put a brightness level in the brackets as a parameter to the setBacklight command - if that is the case, then a nice touch might be to create another for...next loop with values 0 to 255, and turn the lcd backlight on by incrementally 'fading it up'.
Line 11 sets a delay before the next instruction is started.  The parameter value of 125 is the number of milliseconds for the delay.
Lines 12 and 13 turn the backlight off again, and wait for another 125 milliseconds
So, effectively, one iteration of the loop will take a total of about 1/4 second (250 milliseconds), and the loop will run 4 times.  The upshot of this block of code flashes the backlight on and off 4 times in a second.  It will actually take very slightly more than 1 second as each instruction may take a few milliseconds to execute - and external devices may take some additional time to react.

Lines 17 and 18 make sure the block and underline cursor options are switched off.  I don't want the display to have any cursor at all - just text and numbers that change as required.

Lines 22 to 24 are setting up the pushbutton for the control of the backlight.  First we have a variable which just stores the current state of the light (starting with 'off').  The next line tells the Arduino microcontroller that the pin we are connecting the switch to (pin 8) is to be treated as an input to be read from, as opposed to an output that can be written.  The final line of the three sets up a variable that will be used to store the milliseconds counter at the point the button gets pressed.

Lines 27 to 42 are 4 sets of 4 commands that set the cursor position on the display (char, row); write out a line of text (note - the hyphens will be overwritten with the actual values later); write character #223 from the 256 standard ASCII character set (actually, only 128 characters are official standard characters, followed by 128 unofficial character slots) - I have seen 223 listed as the degree symbol (°) for the character set used in the LCD controller, but also seen 176 used for it elsewhere, so this will need some testing when the actual display comes,  The final line in each group of 4 writes the C for Celsius.

Line 46 - OK, this might take some explaining!
When an analogue input pin is read, it is normal (actually, it is required) for the voltage on that pin to be somewhere in the range of 0 to 5 volts.  As the chip itself is also running from 5 volts, then that is a handy thing for it to be able to use as a comparison for the voltage present at the analogue input.
The voltage seen at the analogue pin is expressed in 1024ths of the reference 5v - i.e. an integer 0 to 1023, where 0 = 0v and 1023 = 5v.  This gives the smallest increment a value of 5v/1024 = 4.9mV.
Since the LM35 produces an output of 10mV per degree C, then this effectively means that our thermometer can only differentiate temperature in half degree increments.  However, also note that our LM35 will only ever put out a maximum of 1.5 volts, and the 'normal' usage range in this instance will have it somewhere between 0 and 0.5 volts - so comparing it with a 5 volt reference with half a degree accuracy is somewhat wasteful.
The great thing is that internally, the chip also has a 1.1v reference available to it.  If that could be used instead of the normal 5 volts, then each of the 1024 increments now becomes slightly over 1mV, meaning that our thermometer can now read in 0.1 degree increments.  However, if the temperature goes above 110C (i.e. the voltage on the analogue pin is higher than 1.1v), then the thermometer will still register it as 110C - that has become the highest recordable temperature.
So analogReference(INTERNAL) is simply telling the chip to use its internal 1.1v reference for analogue inputs, instead of the normal 5v.

NB - there is also an analogReference(EXTERNAL) option, whereby you can provide whatever reference voltage you want (up to 5v max of course) and connect it to the chip's AREF pin.  So, if I had a regulated 1.5v reference, then I could use the full range of the LM35's measuring range, or alternatively, a 0.5v reference would severely constrain the range I could measure, but give a much greater level of granularity with each increment being about half a millivolt!

If you want to read more about the way the analogRead and analogReference are related, then the Arduino Reference is a good place to start (and they explain it far more succinctly than me).

Next up - SK001 - in theory...(pt 4) - the process loop.

Sunday, 14 May 2017

SK001 - in theory... (pt 2)

Here is the sketch, as it stands at the moment, broken down into manageable chunks that I will endeavour to explain, at least from a logical perspective.  This is a biggie to start your Arduino journey with, but having looked at dozens of 'getting started' sketches, I wanted to show something with a real world application.

You may not understand every single line of code, but that's OK - you can look up individual commands in the Arduino Programming Language Reference, or get in touch with me and I'll do my best to explain to the best of my understanding.  So, let's dive in...

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// include I2C library for LiquidCrystal (www.sunfounder.com)
// check the specific display against http://playground.arduino.cc/Code/LCDi2c for correct I2C library
#include <LiquidCrystal_I2C.h>

// declare variables - pin connections
int LM35out=0;                // LM35 output -> pin A0
int lightSwitch=8;            // pin D8 for backlight pushbutton

// declare variables - Min/Max
float minTemp=99.9;           // initially set very high
float maxTemp=0.0;            // initially set very low

// declare variables - Backlight
unsigned long lightOnTime;    // to hold millis() value when pushbutton pressed
char lightState = "off";      // holds current state of backlight (on/off)

// declare variables - Five Min Averages
const int loopPeriod=3000;    // set period as a constant for use in average calcs as well as controlling loop
float fiveMinAccum=0;            // set accumulator to 0
int fiveMinCount=0;              // set counter to 0
float fiveMinAve=0;              // set average to 0

// declare the LCD device variable (7-bit address, chars, lines)
LiquidCrystal_I2C lcd(0x27,20,4);

So, remember that all the lines starting with // are simply comments, and comments can also follow the line of code - on the same line.  I have a terrible memory, so I put LOTS of comments in my sketches.  You might find them helpful to read - I know I do!  Notice that each line of code ends with a semi-colon (;)  If you forget to type them in when you start your own coding, be ready for all sorts of strange problems!

Line 3 is the one that gets the external code library for the LCD display.
The library is called LiquidCrystal_I2C.h

Lines 6 and 7 declare a couple of variables that each hold an integer type of number.  These are actually just to identify 2 of the I/O pins on the Arduino Uno's chip.  It is easier in the program to refer to these pins using a name with some context, rather than having to remember what 'pin 8' is used for, for example.

Lines 10 and 11 set up 'float' type variables.  A 'float' can store a number with decimal places, rather than just an integer. I have initially set the minimum temperature to a high value and the maximum to a low value. The first time I read the actual temperature it is likely to be somewhere between these two, and so both will get overwritten with the actual temperature then.

Line 14 is setting a 'long' numeric variable.  Note that it is also marked as being unsigned.  By default, all numeric variables are signed, which means they can hold negative numbers.  Every numeric type (float, integer, long, and double) has a specific range of numbers it can count to - and for signed types, 0 comes in the middle with numbers spreading out equally on the positive and negative sides.  For example, an 'int' type number can hold the values -32,768 to +32,767.  However, an unsigned int cannot hold negative numbers, but it does still have the same number of numbers, just starting at 0.  So an unsigned int can hold any number from 0 to 65,535. OK, back to line 14 again - this variable is going to hold the count of milliseconds since the sketch started running, when the button is pressed to turn on the backlight.

Line 15 is a 'string' variable.  Instead of numbers, this holds a text value - in this case, it will be simply 'ON' or 'OFF' to indicate to the program if the backlight is on or off.

Line 18 is another integer value, but notice that this one has the word 'const' in front of it.  This turns the variable into a 'read-only' constant value.  Once it is declared here, the value in it cannot be changed elsewhere in the sketch.  In this case, it is the delay to be used at the end of the loop function, before it goes back and starts again.

Lines 19-21 declare some variable to be used when calculating the 5 minute average temperature, which I will explain when we get to that part of the code.

Finally - Line 24..!  What the heck is this doing?
Firstly, note that it is somehow linked to the external code library we  pulled in (line 3) - notice how the line starts with the name of that library (minus the dot-h suffix)?  Next comes a name lcd followed by parentheses with some strange looking numbers separated by commas.  A bit like the parentheses on the function definition we saw earlier, these ones are also holding some parameters.

Remember that the external code library takes lots of complex coding and breaks it out into easier functions for us to use (like my examples 'turn on the backlight' or 'clear the screen')? OK, now what this command is doing is creating a way of linking all those functions to something in the sketch (let's call it an 'object') that we will call lcd, and then telling lcd where my actual lcd is (0x27 is the address of the lcd controller), and how big it is (20 characters, 4 rows).

So now the object we have called lcd has links to all those functions in the library (we call them methods), and also has some parameters that link it to the physical device (we call these attributes).  Once this line has been run, then we can call any of the methods in the external code library, and send them to the lcd like this...

1
2
3
4
5
// turn backlight on
lcd.backlight();

// turn backlight off
lcd.nobacklight();


Phew - that's been quite a bit for me to type, and you to take in - so let's take a break here, and I will post the setup() function in part 3, very soon.

Saturday, 13 May 2017

SK001 - in theory... (pt 1)

At the moment, pending the arrival of my Arduino Uno R3 look-alike from China, this sketch is a theoretical draft or work in progress... it looks good on paper, but who knows if it will actually work!

There are a number of unknowns, such as, will my LM35 temperature sensor (while installed in the module that I salvaged from a burned out UPS) still produce 0-1.5 volts over the range 0-150C, or does that op-amp chip actually change the voltage?  Will it even work at all?  Will the LCD library I found work with the LCD I purchased (I've seen dozen's of forum messages from people unable to get their LCDs to function - which usually comes down to addressing or library compatibility problems).

The sketch has gone through a number of iterations, and evolved somewhat from the original... but the nuts and bolts of the original concept were...
  1. Include LCD library, and declare variables.
  2. Setup
    1. Initialise LCD
    2. Write static information to LCD
  3. Loop
    1. Check temperature - write it to LCD
    2. If it is higher than Max, then replace the value in Max and write to LCD
    3. If it is lower than Min, then replace the value in Min and write to LCD
    4. Wait a few seconds, then go back to start of Loop
Hopefully, this is not too difficult to follow.  Arduino sketches all have this basic structure of three segments.  

The first is where any external code libraries are identified to the sketch.  A code library handles all the complex instructions for a device we want to use (such as the LCD), and breaks them out into simpler functions (like 'turn the backlight on' or 'clear the display') that we can use more easily in our own code.

Variables can also be declared here.  Variables are just named buckets to hold a particular piece of information, like the current temperature, or the highest temperature that has been seen so far.  If you declare a variable here, it can be used by code anywhere in the sketch. 

The next two segments handle the setup of everything that is needed to make your sketch work, and then a repeating loop that runs the actual process itself.  Arduino sketches use a code construct called a 'function' for these two segments.  A function is a named block of code, designed for a specific purpose, that can be called from other sections of code.  The block of code can receive input parameters, and/or return an output value at the end - but both of these are optional, and for these two special functions in every Arduino sketch, neither option is used.

This is how you start to define a function...

1
2
3
output_value function_name(input_parameters) {
   // Your code goes here
}

Because these two special functions have no input parameters, there is nothing between the parentheses, and as there is no output value, then this is noted by using the word 'void'.  So, the two special functions look like this when you first open a new sketch... (in case you hadn't figured it out, the lines starting with // are comments).

Here you can see two functions - one called 'setup' and one called 'loop'.  An Arduino sketch always expects these two functions, even if they have nothing in them!  Setup is run just once at the beginning, and then loop is run.  At the end of all the code in loop, it will 'loop' back round to the start and run all the code again, and again, and... well you get the picture.  Pretty much the only way to stop it, is to pull the plug!

You can write your own functions in addition to setup and loop, but we'll get to that concept a bit later.  Note that variables can also be declared inside a function, but if they are, then they are only available for use within that particular function.

My original code was written with a 16 character by 2 line LCD in mind.  But when trying to fit the current, minimum, and maximum temperatures on the display, each with meaningful labels, I realised that 16 by 2 provides a very limited amount of display real-estate. That was what triggered me to opt for the larger 20 by 4 LCD in my shopping list instead, and that was the first of my many code changes.

I then found how I could improve the accuracy of the mechanism to read the temperature value; how to format the output to 1 decimal place (and include a leading 0 if required); added a button press trigger to turn on the backlight for 10 seconds without interrupting the real-time reading of the sensor; and finally, added a function to calculate an average value over 5 minutes.  This last improvement was done ready for the next phase of my experimentation, which is to use this as a data-logger and either write data to an SD card, or directly back to my PC (where it could be displayed graphically, for example).  I've also found an alternate mechanism to turn the backlight on and off by degrees of brightness - but I don't know if my display will actually support it.

Well - this post has rambled on rather, so I'm going to call it a day for now.  I'll post part 2 in the next day or two, which will include the whole sketch as it exists at his point (remember - it is untested!!), and will break the code down with some explanations of what each bit does - or is intended to do.  

Friday, 12 May 2017

Delay(2592000000);

Now I've placed the orders for all the stuff on my shopping list, I have to wait roughly a month for it all to be delivered from China - for those of you that already know a little C++ Arduino coding, you will recognise the significance of the title above - for the rest of you, it is the command to tell the Arduino to sit around and do nothing... for a MONTH! Ok, ok... if you wanted to get real technical, it probably wouldn't work because the millisecond counter that is used to control delays, runs out of digits after around 9 hours, and resets back to zero, so it would never reach 2.6 billion (or 2.6 thousand million, depending on where you grew up).
edit :  since writing this, I have discovered that the millis count doesn't reset every 9 hours or so as I originally thought - apparently that figure was a throwback to an old version from around 2007!!  The current millis counter will run a little short of 50 DAYS before wrapping around to 0 again - so I COULD create a delay for a month, after all!!
So, being the imaptient soul that I am, I decided to start work on developing my first sketch (that's what an Arduino computer program is called) so I have some practice with the code structure and also using the development interface (IDE).  Here is the inspiration behind it...
One of the things in my scavenger box is a temperature sensor that I recovered from an old piece of computer equipment that died in rather spectacular fashion - it caught fire!  The sensor itself was a remote plug in device used to monitor temperature in another area - so it was not damaged by the fire.
I've dismantled the unit and after some time trying to figure out how the components were connected together, I found the circuit board inside to be relatively simple (at least conceptually).  The key component is an LM35 temperature sensor, and this is sandwiched between a voltage regulator and an op-amp chip.  "Whoa!!" I hear you say... what language is this?  OK, let me explain what I know - which isn't everything.
LM35 sensor annotated_lowres
LM35 Temperature Sensor - this is a device which needs to be fed on a diet of anything from 4 to 30 volts DC, but ideally 5 volts - which is the commonly used voltage for most electronics devices .  It will operate over a range from -55C to +150C.  The device reacts to temperature, and outputs a voltage that is directly proportional to the temperature.  The specs say that the LM35 will put out 10mV (10/1000ths or 1/100th volt) PER degree centigrade.  So if it is 100C, then the LM35 will put out 1 volt.  If it is 25C, then the LM35 will put out 0.25 volts.  How does it measure temperatures below freezing?  This is where the waters get a bit muddy for me, and remember - this is currently all theory with no physical observations or experiments to back it up.  Perhaps it puts out a 'negative' voltage? I am not sure, and for the moment, am not going to stress about it - the part of Australia I live in never experiences sub-zero temperatures, and I doubt I will be putting it in the freezer to test (ha ha, well actually, I almost certainly WILL at some point in the future, out of curiosity, but just not right now).
Voltage Regulator - This device acts a bit like a filter.  It allows you to throw almost any kind of DC voltage at it (within the design constraints of the chip itself), and outputs a nice clean fixed 5 volts DC that the rest of the circuit can use.  So for example, the whole sensor unit could be receiving an unconfirmed and wobbly voltage somewhere around 20-25 volts into the voltage regulator, but we can rely on the LM35 getting exactly 5 volts.
Operational Amplifier (Op-Amp) - I need to research just how these things work, but it is basically some kind of amplifier for the output from the LM35.  You will see from the datasheet, that the LM35 can only supply the output voltage to a maximum of 10mA which is a fairly tiny amount of power - enough to be monitored, but not enough to drive another device.  Since the voltage output of the LM35 is significant, I assume that this amplification step will just increase the available current of the output, as opposed to changing the voltage - but this needs to be confirmed.
Now I understand roughly how it works, I am going to attempt to use the whole sensor unit as it stands with my Arduino, but first, I will run some experiments with it connected to my breadboard, just to confirm it operates as I suspect.  Otherwise, I may just desolder the LM35 from the rest of the board, and use it on its own.
In the next post, I'll describe my first sketch - a Min/Max Thermometer, and how it has developed from my original concept, into something a little more sophisticated.

The Shopping List

So, I settled down to an hour or two of eBay hunting, to cost out what I thought I would like to buy, and then decide if I could justify it using the 'number of coffees I will need to go without' equation.  Unfortunately, this turned into a couple of long evenings - I don't know if it's just me, but I have an awful lot of trouble with eBay - it seems sooooo slooooow to load up any page, and you try doing a search for Arduino Uno, and just see how many pages of results it throws up!!!
The next issue I had was that each listing often covers multiple product options, so there is no point sorting them by price to try and find the cheapest... When you open the listing that appears to be offering an Arduino Uno for just $1, you'll find that the $1 is just for the USB cable, or the header pins or some other optional extra - rather than for the board itself.  So sorting through all the 'false' adverts made this an incredibly slow and laborious task.  But hey, I'm a skinflint, so 2 hours of searching to save me 25c is well worth the effort.
I decided that I definitely wanted a breadboard, but I wanted one that's a reasonable size, not one that requires artful planning once you exceed 5 components...  I opted for a model called MB-102 which has 630 points in 126 blocks of 5, and then another 200 in + and - bus bars.  To go with this, I also decided that I needed connector wires in M-M/M-F/F-F formats, and a regulated power supply unit for the breadboard (well, it was only $1).  The power supply can run from a USB lead or from an external power source (such as the various mains adaptors I have salvaged from all number of devices over the years).  I also found a 9v battery lead with the same 2.1mm plug as the mains adaptors, which means I can also run the power supply from a 9v battery.
Then, I started drooling over the Arduino Uno listings, which inevitably brought up huge numbers of ads for add on sensors, shield boards, and all sorts of goodies, but first let me give you a few warnings about the ads for Chinese clone Arduino Uno 'compatible' boards.
  1. They all seem to use a different USB controller chip (CH340) from the genuine Arduino (ATMEGA16U2).  This chip may need you to find and load the specific drivers for it, before you can plug in your Arduino (I will report back on that when mine eventually arrives).  It also means that they may have USB Mini or USB Micro sockets on them, rather than the Arduino's USB-B.  HOWEVER, I found lots of listings where the USB option described in the listing title did not match the pictures, or the pictures and detailed description were different (or even all three were different).  Sometimes a single listing even showed pictures that conflicted with each other.  Since the boards are frequently sold without a cable, it would be nice to know definitively what cable you need to buy without having to wait for the board to arrive.
  2. Because the whole concept behind Arduino is that it is open-source, the clone boards may have additional 'enhancements', which can be very ambiguously described in the ads using very poorly translated Chinglish.  For example, in the listing that I eventually chose to purchase from, it mentioned an extra two rows of holes for the pins AND an extra three rows of holes for the wiring (clear?), and if you looked closely you could see that the pictures showed at least three different variants of the board from different manufacturers, that included varying configurations of extra pinout options (rows of holes) - so I am not really entirely sure what is going to turn up in the end...
  3. Another 'enhancement' is that they mostly seem to use a slimline 'surface mounted' version of the main chip, which is soldered directly to the board.  If you mess up a circuit and fry the chip, you will have to buy a complete new board.  The genuine Arduino uses a replaceable chip that loads into a socket.  Mind you, when the entire Chinese clone board costs little more than the replaceable chip - I guess there is not much advantage lost.
  4. Be careful to understand exactly what you are getting in any listing - make no assumptions.  I saw boards with or without cables; with header pins already installed, or with them separate, or with them as an 'extra'; with USB-B, Mini, or Micro sockets - or with ambiguous descriptions leaving you not really sure; with different pinout options (all over and above the 'standard' minimum, but still a bit frustrating).  If in any doubt contact the seller - but don't count on getting a clear or definitive answer from them either.  They often have a poor grasp of English, and are not necessarily even specialist electronics suppliers - they just buy and sell random 'stuff'.
Anyway - having said all that, I finally plumped on a particular listing, for a board with header pins (hopefully already installed), and three additional sets of pinouts designed for specific interfaces (again - hopefully), but without a USB cable.  According to the listing title, it has a USB Mini socket, but working in IT, I have access to dozens of spare USB cables of pretty much every configuration, so even if I don't have the correct cable for it at home, I am 99% sure I will be able to loan one from work until I can get what I need.
The final thing on my list is an LCD (text) unit.  The most commonly available option seemed to be 16 characters by 2 lines, but I opted for a 20 character x 4 line unit, that only cost an extra $1.50.  Something to watch for on these, is whether or not they have the IIC (I2C) backpack on them.  If they don't, then you will need at least about 8 or 9 data connections, plus power to hook them up, but with an I2C backpack, you can run them with just 2 data and 2 power connections.
So my final shopping list looks like this...
  • Breadboard MB-102 - $2.59
  • MB-102 Power Supply - $1.00
  • M-M/M-F/F-F cables (40 each) - $4.42
  • 9v Battery Leads (x2) - $1.00
  • Clone Arduino Uno R3 (USB Mini) - $4.42
  • LCD2004-I2C - $5.88
  • TOTAL - $19.31 (or just under 5 coffees)
Update - 3 weeks later... only the breadboard and cables have turned up so far, but I've been using my time productively, as you'll see in my next post.