Tuesday, 25 July 2017

Photography Flash Trigger - Pt 2



Last night, I did a bit more work on the Flash Trigger project.  The first thing was to remove the testing printouts to the Serial Monitor, and replace it with the LCD, and then I mounted the receiver and LED on either side of a bit of card to try it out as a reflective receiver.  That was much easier to keep lined up with the dripping tap.


I've modified the loop() logic so that when the beam is interrupted, this triggers a quick beep, puts a message on the LCD, and starts a 3 second countdown. During the countdown period, the LED (on pin 13) will still flash to indicate when the beam is broken, but nothing else will happen.  After 3 seconds, it all resets, and the trigger's ready to go again.  I also added a counter to keep track of how many times the trigger is.... well, triggered.  The latest version of the code looks 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
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
#include <NewTone.h>
#include <IRremote.h>
#include <LiquidCrystal_I2C.h>  
  
#define PIN_IR 3    // Documentary value only - Pin 3 (OC2B) is hard-coded in the library.
#define PIN_DETECT 2
#define PIN_STATUS 13
#define PIN_TONE 9

// Instantiate objects
IRsend irsend;
LiquidCrystal_I2C lcd(0x3F,20,4);

// set up global variables
int triggered = 0;
long trigMs = millis();
int trigCount = 0;

void setup() {
// Set pins as IN or OUT
  pinMode(PIN_DETECT, INPUT);
  pinMode(PIN_STATUS, OUTPUT);
  pinMode(PIN_TONE, OUTPUT);

// Put prompt on display (Coordinates = col 0-19, row 0-3)
  lcd.begin();
  lcd.setCursor(0,0);
  lcd.print("IR Beamer waiting...");

// Start the IR LED sending  
  irsend.enableIROut(38);
  irsend.mark(0);
}

void loop() {
  // Check beam and update LED in real time
  int beamState = !digitalRead(PIN_DETECT);
  digitalWrite(PIN_STATUS, beamState);

  // If newly triggered...
  if(beamState == 1 && triggered == 0) {
    // set flag and note the time
    triggered = 1;
    trigMs = millis();
    trigCount++;

    // beep
    // NewTone(PIN_TONE,750,250);  // The beep gets quite annoying

    // update LCD
    lcd.setCursor(10,0);
    lcd.print("TRIGGERED!");
    lcd.setCursor(1,1);
    lcd.print("(Counter=");
    lcd.print(trigCount);
    lcd.print(")");
    lcd.setCursor(1,3);
    lcd.print("resets in 3 seconds");
  }

  // Once triggered...
  // If 3 seconds have passed, then reset
  if(triggered == 1 && (millis() > (trigMs + 3000))) {
    lcd.setCursor(10,0);
    lcd.print("waiting...");
    lcd.setCursor(1,3);
    lcd.print("                   ");
    triggered = 0;
  }
  // If 2 seconds have passed, update countdown  
  else 
  if(triggered == 1 && (millis() > (trigMs + 2000))) {
    lcd.setCursor(11,3);
    lcd.print("1");
    lcd.setCursor(19,3);
    lcd.print(" ");
  }
  // If 1 second has passed, update countdown  
  else 
  if(triggered == 1 && (millis() > (trigMs + 1000))) {
    lcd.setCursor(11,3);
    lcd.print("2");
  }
}


I checked it out with the tap, and it was working pretty well, as long as it was within about 3-6" of the drips.  When IR remotes work from the sofa to the TV on the other side of the room, I was quite surprised that the range on my little contraption is only a few inches, but it is good enough for the particular job I have in mind.
The next step is to link the trigger action to an actual external device via a photocoupler - and then finally, introduce a delay.

By the way, the header pins that I ordered turned up at the end of last week, and the supplier also sent me a replacement set in case the originals had gone missing, and they turned up this morning.
Also in the post this morning was part 1 of my regulated power supply project - the buck converter - which has arrived 2 days before the estimated timeframe!  I must connect that up to some of my supplies and give it a bit of a test before I leave feedback for the seller.

With all these envelopes turning up, I felt a little like Julian Ilett and his " It's Postbag" videos on youtube.  Speaking of which, I was a little concerned when I found he has done a video about 'fake' LM2596 boards exactly like the one I ordered from ebay... thankfully, it turned out that what he was describing was sellers claiming to be selling the High Voltage (HV) version of the LM2596 which is rated at up to 60VDC input, but only putting the standard LM2596 (relabelled to look like the HV version) on the board, which is rated to 40VDC, or putting a fake 65V capacitor on the input side of the circuit (looked like the standard 50V cap with overprinting) - so the boards worked OK up to around 40-45V as per the design of the standard components, but then started behaving very strangely, and sometimes smoking gently, at anything higher.  Hopefully, as I have bought the standard 40V model, with the intention that it will never experience inputs above around 20VDC, there should not be a problem...

Sunday, 16 July 2017

Photography Flash Trigger - Pt 1

Unfortunately, despite the seller's promises of a very quick ETA for the header pins I ordered at the beginning of the week, which were supposed to arrive Thursday or Friday, they have sadly failed to deliver - quite literally.

So this weekend's I2C LCD interface cable project was sidelined and I decided to do a little testing towards my photography project instead.  The idea of the project is to have an optical beam of some sort, either IR or visible light, which can act as a trigger for my camera flash when the beam is broken.  The aim being to be able to accurately capture water droplets as they hit a surface and break apart or bounce, etc.

To account for how far the drop needs to fall, I also want to have a variable resistor on an analogue input, that can be used to introduce a delay between when the beam is broken, and when the trigger occurs.With 1024 points on the analogue input scale, I figure that each point can be a millisecond and that will give me up to a second's worth of delay adjustment (or a little over).  The range I anticipate needing would most likely be no more than half a second, so I could possibly even go for 2 points per millisecond.

Of course the first step is to get the beam part working, and that's what I experimented with today.  I used Ken Shirrif's IRremote library to get the basic functionality, as I found a post on his blog with sample code for a basic beam-break  sketch using an IR LED and VS1838B receiver.  I know there is a newer library available, but it comes with warnings that it is not backwards compatible in some areas, and when I tried it with Ken's code - I got errors that I couldn't fathom so decided to just use his library instead.

A word of warning here - there are some conflicts between his library, and the Arduino RobotRemote library that comes as standard with the IDE install.  You will need to move or delete the RobotRemote library to use IRremote without errors.

There are 2 parts to the sketch - the first part is to set the IR LED constantly sending out a beam.  IR LEDs can draw a lot of power, and also generate heat - so the standard practice is to use a 50/50 pulse width modulation (PWM) at 38KHz.  All modern IR remote controls conform to this standard, though some older ones use 56KHz, but for the same reason.  The IRremote library takes care of this for you, so you don't need to work out all the PWM parameters and timings.
1
2
3
4
5
6
7
8
9
#include <IRremote.h>

#define PIN_IR 5    // Only seems to work when connected to pin 3 regardless of this setting
IRsend irsend;

void setup() {
  irsend.enableIROut(38);
  irsend.mark(0);
}

In his sample code, Ken has line 3 above set to PIN 3.  I wonder if this is more documentary than actual requirement, as when I tried to use pin 5, it would not work.  However, I plugged the LED into pin 3 and it worked despite my sketch having PIN_IR set to 5.
Line 4 instantiates an IRsend object, and calls it irsend.
Line 7 and 8 are in the setup() routine, so are only done once.  Line 7 sets the IR PWM frequency to 38 (KHz), and line 8 sends a 'mark' - which seems to be the IR equivalent of setting a digital output pin high.  The parameter on the mark action is a duration.  IR remotes work by sending on and off sequences, a bit like morse code, but with varying length ons and offs.  The duration parameter defines how many milliseconds the 'on' should last.  0 indicates that it will stay on indefinitely - which is just what I need for my beam.

The second part of the code concerns the receiver which senses the beam, and recognises when it is broken.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#include <IRremote.h>

#define PIN_DETECT 2
#define PIN_STATUS 13
IRsend irsend;

void setup() {
  pinMode(PIN_DETECT, INPUT);
  pinMode(PIN_STATUS, OUTPUT);
}

void loop() {
  digitalWrite(PIN_STATUS, !digitalRead(PIN_DETECT));
}

The detector pin is connected to pin 2, and declared as an INPUT
Pin 13 (the on-board LED) is set as an OUTPUT and used to indicate the status. It will flash on when the beam is broken. I also added monitoring via the Serial Monitor, and set a buzzer to beep, but for now, this is the pure IR code only.
Line 13 in the loop() routine is the key here - it checks the value on the detector pin (2) and uses that to control the on-board LED.  Under normal circumstances, the detector will be receiving a constant signal from the IR LED, but we don't want the on-board LED on all the time, so we invert the value, by preceding it with '!'  That way, the LED flashes on only when the beam is broken.

One thing I found with this sketch, is that the trigger actually occurs not at the point in time that the beam is interrupted, but at the point the beam is re-instated.  I could place my hand between the IR LED and receiver, and nothing happened... until I took my hand away and the beam was able to hit the sensor again.  For my application, I don't foresee this being a huge problem, but in other applications, it could be an issue.

The two extracts above were combined together and gave me the basic sketch, but I felt that an audible feedback would be useful, so I added a tone() command in the loop() routine.  Uh-oh, now my sketch will no longer compile!  Something about multiple definitions of vector7..???

I had no idea what this meant, but it seems many people have experienced it.  It is something to do with the on-board timers inside the ATmega328 chip, and how they are used in people's libraries.  There are only so many timers, and a lot of libraries - so it is inevitable that eventually, you will come across 2 libraries you want to use together, that are trying to use the same timer for different things. In my case, I think it was the timer being used to control PWM for the tone() command conflicting with PWM for the IR LED.  Luckily, I was able to find an alternate tone library (NewTone.h) that uses a different timer, and this got rid of my conflict.

So here is my complete code with Serial Monitor and bleeper included.

 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
#include <NewTone.h>
#include <IRremote.h>

#define PIN_IR 5    // Only seems to work when connected to pin 3 regardless of this setting
#define PIN_DETECT 2
#define PIN_STATUS 13
#define PIN_TONE 9

IRsend irsend;


void setup() {
  pinMode(PIN_DETECT, INPUT);
  pinMode(PIN_STATUS, OUTPUT);
  pinMode(PIN_TONE, OUTPUT);
  irsend.enableIROut(38);
  irsend.mark(0);
  Serial.begin(9600);
}

void loop() {
  int beamState = !digitalRead(PIN_DETECT);
  digitalWrite(PIN_STATUS, beamState);
  Serial.println(beamState);
  if(beamState == 1) {
     NewTone(PIN_TONE,750,250);
  }
}

I set it all up on the breadboard, and it worked OK, but I really wanted the LED and sensor on a longish lead so that the Uno and power, etc, can all be far away from any water splashing.  So, I've now wired them onto a half meter length of 6 core cable, with two cores going to the LED and three to the sensor, and terminated them onto a new little DIY break-out board for convenient connection to the breadboard.

However, having got this far, I am not convinced that the IR LED and Receiver is going to be the right solution.  It does not seem to react very quickly at all... I can easily pass my finger between the LED and sensor and it not trigger the beep or flash the LED on pin 13.  I am also not convinced that a water droplet will provide enough of a barrier to the IR light to actually interrupt the beam in the first place.

Supplemental - Well, what do you know?  It actually works, well, to a degree anyway... The somewhat shaky hand held alignment of the LED and Receiver was introducing a lot of false triggers, but when I could hold them still enough relative to each other on either side of a dripping tap, I was rewarded by a fairly consistent beep... beep... beep!  This means I would need to jury-rig some kind of frame to hold the two devices in alignment to each other, and also be able to place that securely in position around the dripping tap.  However, this is just the first of a few alternative ideas I want to try, the next being a reflective beam contact, as opposed to a beam break - so I will arrange the sensor and LED adjacent to each other both facing in the same direction, but separated by a barrier, the idea being that as the water drop passes, it reflects the IR beam back to the sensor.  It should be easier to construct a rig for this arrangement - if the concept works.

By the way - I had a surprise package arrive from China today - the replacement MB102 PSU whose regulator became deregulated.

Monday, 10 July 2017

Shopping list 2

Last night, I ordered a few more items from some of eBay's abundant "Free postage, but two month wait time" sellers in China.

As noted in a previous post, I discovered that pretty much all of my my power supplies, are unregulated, and deliver about 40-50% over-voltage when under no, or light loading.  The same will be true if I ever get around to using a solar panel to charge my 12V batteries.  So I've decided to build a simple regulator box, that can take anything up to 20V (my biggest mains power supply units are 12V - putting out around 18V, and a 12V solar panel can also get up to around 18-20V under ideal conditions.), and put out a regulated 9 or 12V, which can then in turn be safely put through either the Arduino Uno or MB102 PSU on-board regulators, to provide 5 or 3.3V to my circuitry.

I've found a 'buck' converter based on an adjustable output LM2596 regulator, for just $1.25, that takes anything from 4.5 up to 40V input, has a fully adjustable output ranging from 1.5 to 37V (so appears to require an overhead of 3V), and can handle up to 3A of current drawn through it - but I'll hopefully never need anything like that amount.  This will be the core of my regulator box.

I found some 3-30V 3-digit LED voltmeters, for $1.20 each, and I am going to wire one of those on each side of my regulator, to monitor the input and output voltages.

I will put a 2.1mm jack on the input side (salvaged from one of many old bits of hardware in my 'junk' box at work), to take power from the mains DC units, but at the moment, I'm not sure what to do for the connections on the output side.  Obviously, I want the regulated voltage to feed the Uno, but as the Uno has a limited potential for current, it would be nice to also be able to tap directly off the regulated supply for higher powered devices, such as fans and motors.  So maybe I'll have 2 output options - a simple 2.1mm plug that can connect to the Uno and MB102 for low power control circuits, and then the option to tap directly into the regulated voltage at higher amperage if required.  Just not sure what kind of connection to use for this - perhaps some form of PCB mounted pin connector that I can then connect to the breadboard using DuPont cables..?  It would be nice to mount all this in a little jiffy project box, and then mount it onto a board with the Uno, LCD, and breadboard all permanently mounted as a mini workbench.

The next item I've purchased is a Real Time Clock (RTC) module, based on the DS1302 chip.  I've read a few warnings saying they aren't the most accurate of devices, but to be honest - I don't need something that is accurate to less than a microsecond over 10 years... I just get fed up having to keep type in the seed time and date for any time-based projects while I am testing them.  So for $1.00, even if it loses a few seconds a day, it will do for my needs... maybe I can get some sort of wireless connection next time, that will allow me to reset the clock time from my PC or the internet once in a while.

My final purchase, was a bundle of header pin strips.  I got one set with my Uno clone, but have already used about half of them on my little breakout boards and for other testing, and that's without putting any on the Uno itself.  I've got a bundle of ten 40 pin strips - should keep me going for a while.

So, now I just need to sit back and see how long they take to arrive.  I placed all 4 orders last night 9th July, and 3 of the 4 claim to have 'sent' the items already, two with an ETA of 'before August 8th' (WOW - just ONE month!) and one (sadly, the regulator itself) not until September 1st.  I guess that means they have been packed and thrown into a 40ft shipping container on a dock somewhere, and when the container is full, it will be loaded onto the next cargo carrier coming our way...

The final item (RTC) has not yet been sent, though ironically, its ETA was the earliest at August 3rd!
I'll update this post as and when the items arrive.

11/07 - I got notified overnight that the RTC has now also been despatched, with an ETA of August 2nd.  Today, I also made one more purchase, this time of some 1x4 female header pin PCB connectors.  Thinking ahead to mounting all my gear on a little workbench, I want to utilise the extra bank of optional connection points for power and SDA/SCL, and build a custom semi-permanent I2C connection cable.  I could have ordered a pack of 10 of them from another Chinese supplier for $1, and waited till September, or pay $2 for the same pack of 10 and have them on Friday... I'm getting a little fed up with waiting for China's free "Economy International Post" option, so I paid the extra dollar and will be able to put up a post showing my upgrade at the weekend hopefully ;-)

25/07 - Ironically, the LM2596 regulator - the item with the latest ETA - arrived today, two days ahead of the start of the potential delivery period, and a full 5 weeks ahead of their final ETA date!


Testing tone();

At the weekend, I quickly cobbled together another of my little break-out boards, this time for a piezo crystal transducer (or a buzzer to you and me). 

One thing this taught me is that although we all call these things buzzers - actually, they are just 'clickers'.  If you apply voltage to it, it just goes click.  The tone comes from applying and disconnecting power 1000 times a second, to get a 1KHz tone for example.  A real ''buzzer' has components and circuitry built into it, so that all you need to do is apply power, and you get a nice tone generated.

Anyway - I digress... That was not really the point of this post.  I found out something else, which wasn't really immediately evident to me from the Arduino Reference manual, and even looking at it again with hindsight - I still didn't find anything about it...

To generate a tone on an Arduino, simply connect a 'clicker' thingy (I'm just going to call it a buzzer) to an output enabled pin and GND (NB - the buzzers are polarity sensitive, and have the positive pin marked on the casing, or indicated with a red lead), and then there is a nice easy tone command that does the work of generating the square wave for you.  Typically, the code looks something like this...

// start sending the tone of toneFrequency Hz to the device on buzzerPin
    tone(buzzerPin, toneFrequency);
// keep doing it for the period toneDuration milliseconds
    delay(toneDuration);
// at the end of the toneDuration delay, stop sending the tone
    noTone(buzzerPin);


I was using this code in a little sketch to make a hospital heartbeat monitor type 'beep', and also flashing the LED in time with the beep.  It all worked fine, and I should really have left well alone at this point (if it ain't broke, don't fix it!)

But then, I found an alternate version of the tone command, that includes the duration as a third parameter.  Why would anyone go to the lengths of typing 3 lines of code, when one will do..?  So I tried it...

// Same function, but all in one command
  tone(buzzerPin, toneFrequency, toneDuration);
// No need for the delay() or noTone() commands!


It worked!  Well, at first glance, it seemed to have worked, but then I noticed something odd - my LED was no longer flashing.  Actually, it was, but only for the briefest of moments, and unless you covered up the adjacent power LED (yeah - ALL the LEDs are clustered together on my clone - and very bright - it's a bit of a pain sometimes) you really wouldn't notice it.

I wondered about the potential for the buzzer to be drawing too much voltage and not leaving enough for the LED, so I disconnected the the buzzer.  The LED was still only giving the faintest of flickers.  I wondered if the 100ms period was too quick, though my gut was already saying "of course it's not".  I went back to basics and installed Blink, and changed it to 100ms.  Yes it was fast, but the LED lit up properly.  So I went back to my sketch and changed the toneDuration to 5000 millis. No change in the LED, but something else odd was now happening - the normal round-trip duration of the loop() section of my sketch was about 3 seconds, including a 1 second delay at the end. I was expecting my tone to now run for 5 seconds, followed by 1 second pause, but it was running constantly.

Then, the penny dropped - including the toneDuration as a parameter on the tone command, appears to switch it to background operation.  That means that the sketch starts generating the tone, and can carry on with the rest of the lines of code while the tone is being sounded.  So, what was happening is that my LED was switching on, staying on for probably just a few microseconds while the tone command was being evaluated, and then switched straight off again.  furthermore, if the toneDuaration value exceeds the time it takes for the rest of the code in loop() to run through - including any delay() commands, then the 'next' tone starts sounding before the first has finished, so it sounds like it is going constantly.

So - just be a little bit careful with the two flavours of the tone command - they are not the same beast at all, and make sure you know whether you want the duration of your tone to play an integral part in the progression of your code, or if it can be set off and left to run independently while your code goes and does something else...  If you choose the latter option, make sure there is room in your loop() code for the tone to finish before the loop does.

Monday, 3 July 2017

A little DIY, an AHA and a D'Oh!



I got the soldering iron out at the weekend, and what do you know, I managed to fix my LM35 temperature sensor after all.  I used a small bit of Vero stripboard and soldered the outer two legs in place on tracks 1 and 3, with the body laying down almost flat on the board.  Then I got a short bit of bare jumper wire bent at a right angle, and soldered it to the middle track while the upright part was leaning against the base.  The soldering of that upright to the body of the chip was the tricky part as there wasn't much room between the other two pins to manoeuvre the tip of my soldering iron, or for a clumsy blob of solder that might short the pins out. Thankfully, there was just enough exposed bare metal left over from the broken leg for a drop of solder to actually take and make the connection.  A 3 pin section of header pins completed the job - and Wahay!  I have a working sensor again.

My soldering isn't too pretty, but it got the job done.  The only thing I wish I could have done was solder on the underside, so that I could write pinout information on the bare side of the board and have it visible when plugged into the breadboard, but I couldn't figure how to solder the header pins that way.  With hindsight, maybe what I should have done was push the black plastic clip right to the end of the pins, and then feed them through from above for soldering.  After soldering, take the black clip off the top and slide it back on the underside again.





Of course, to test the sensor, I needed a 5V supply on the breadboard, and remember - it appears that the 5V regulator on my MB-102 PSU is blown.  However, the original board that the LM35 was on had its own 5V regulator.  So after my soldering success, I decided to build myself a little regulator board too.  The regulator is an L78L05, and it had a 22µF capacitor across the Vin and GND pins. It seemed a simple little project, and with just two components and 3 header pins, it was actually quicker than repairing the LM35.

To test it all, I connected a 12V battery to the PSU.  The 5V output was actually putting out nearly 9V, so I put that through my new DIY regulator and that gave a nice steady 4.98V.  I connected that to my LM35 and got an output of around 180mV, which went up and down as I breathed on the sensor (it's pretty cold here in Sydney at the moment!), so I was quite happy that my DIY repair appears to have worked.  Note the mV value here - it should have triggered some thought process here - but I missed it... read on.

Later in the evening, I loaded up my Min/Max temperature sketch, and connected the sensor to the Arduino - it certainly seemed to be working, but was giving readings of a balmy 23-24C while I was sitting there feeling my toes going numb.  I tried changing the reference voltage between 5V and 1.1V, and tried running from a battery rather than USB, but each change seemed to be giving me all kinds of different readings.  I checked the datasheet again, and then had one of those blinding flashes of the bleedin' obvious - a real slap on the forehead "d'oh!" moment...

The sensor generates 10mv per degree C, but nowhere does it say that 0V = 0C.  The chip is supposed to have a range starting at -55C... so of course - that must be the 0V point.  This means that even at freezing point, the output pin should be reading 550mV.  I had assumed that 0C would generate 0V and somehow -55C would generate a -ve voltage!  Well it made sense at the time, but looking back - how stupid was I?? (Hah - remember this statement)

So with this new 'understanding', I looked at my calculations and figured that all I needed to do was offset my calculations by the 55 negative degrees (but by subtracting 5.5 from the final answer?)...  Yeah -OK, it was late at night, and the 55 degrees got all muddled with the factor of 10 (mV per degree) in my head, and suddenly, my calculations were coming up with temperatures almost exactly matching the digital thermometer I was using as a reference.


1
2
  // Calculation for analogReference(INTERNAL) at 1.1v at .1C resolution
     float celsius=((1.1 * analogVal * 100.0) / 1024.0) - 5.5;

BUT this was all completely wrong and purely coincidental, and having revisited it yet again this evening, I'm still not convinced I am understanding either the calculations or even operation of the sensor.  A fact I had previously missed entirely is that there is more than one way to wire up the LM35 - in fact there are 14 different wiring examples in the datasheet with pullup resistors, diodes, and all manner of schemes - each of which makes it operate differently and respond to a different range of temperatures.  I have been using the most basic +5V, GND, and signal back to the Arduino.  Apparently, this gives a measured range of 2C to 150C - so there are no 55 degrees of negativity to account for after all.  That means my adjustment of -5.5, while giving me good results, is simply a fudge factor, and there is still an inaccuracy that I need to track down.  However, it does mean that the 180mV I saw while testing my DIY repair out in the garage, makes more sense - it probably would have been around 18C out there.  So knowing the range I have, I now need to know if 2C = 0mv or 20mV...

I think the next stage in my testing is to combine actual voltage readings with the analogVal reading at the Arduino, and manually check the calculations alongside my reference digital thermometer.  Who on earth said that the LM35 was an 'easy to use'?