Wednesday, 3 February 2016

Proximity sensing with dedicated IC chips - making breakout boards

Although the bluetooth dartboard scoring thing isn't actually in place at Nerd Towers, the BTLE4 app is already written (using Unity) and sends each players scores as ascii to the 3-way 7-segment displays


Shonky coder art and "unobtrusive" debug messages? Of course it's not finished. But it works!

But a few different projects have come up recently that require a bit of RnD. So what better excuse to crack out the ferric chloride and show yet more photos of filthy ferric-chloride fingernails?

Here are some proximity sensors that we're playing with


The one on the left, with the silver package and two lenses is pretty much what it looks like - an IR led and photodiode in a single package. The soic chip and the (much smaller) sot-23 chips are Microchip's own versions of capacitive sensing ICs but with built-in, programmable sensitivity.

The eight-pin thing being pointed at is very interesting. It's a full-colour proximity and RGB sensor. So it can detect levels of red, green and blue placed under it. That's really exciting! We've used sensors for detecting RGB colours a number of years ago. Then, we flashed a red, then green, then blue LED and detected the relative amount of light reflected, to decide whether a card was red, green, blue or black. It worked. But it was a bit clunky. And didn't do so well differentiating between, say, a yellow and a green card, or a cyan and a blue one. And a grey card was sometimes detected as black. But then again, sometimes a white one was too. And while that was enough to tell the difference between red, blue, green and black, it wasn't very good to tell the difference between black and white!

All that's an aside though. What we have here are a number of different proximity sensors, which we need to work out how to use, so we can choose the best performing one for the current project. But potentially having access to an RGB colour sensor has got us thinking about a few other ideas that have been on hold for a loooong time.

In the meantime, we need to get these things mounted onto some breadboard-friendly breakout boards. Let's etch....


Normally the sight of a PCB with just the traces remaining and no ground plane or copper pour filling the blank spaces makes us scream. But in this case, because we're working with such tiny pitch components, and want to be sure there's no chance of bridging between pins under the chip, we etched away far more copper than we'd normally do, with a board of this size.

Luckily, the pitch of the silver IR proximity sensor is the same as the SOIC chip, and the pitch of the RGB sensor - although tiny and fiddly - is the same as the sot-23 chip. So all we need to do is etch a couple of breakout boards for each pitch, and get soldering!


A few minutes later (and, ok, a couple of re-tries after messing up the tiny sot-23 components) we had our chips soldered onto the pcb(s). Perhaps the hot air gun might have been useful here, but even on the tiny sot-23 stuff, a line of paste and then raking it out with the tip of the soldering iron was quick and easy,


After breaking the board apart (panellising pcbs isn't such a good idea when they are this small and we've only a junior hacksaw to cut them apart!) our sensors were ready for testing on the breadboard


Tomorrow we need to digest the datasheets and try to get some data out of them. We only need the proximity function for the current project (it's not exactly top secret, but it is for a commercial commission, so we'll stick to generics here for now) but we have a project of our own that we're looking forward to trying out the RGB sensor on, and will get a full write-up when we get time!

Sunday, 31 January 2016

Bluetooth dartboard scoring device

So after doing a bit of "trading" on the darts markets over Xmas and the New Year, it looked like darts could be a fun game. After all, which other game actually requires you to leave the house and go down to the pub for a few hours? With the weather so rubbish in recent weeks, and with petanque (that's french boules to you) regularly being rained off, it seemed like a great idea!

A quick click around on Amazon and thirty quid later, and we've got a spanky new Winmau Blade4 dartboard and a set of Ted Hankey darts on next day delivery!


It didn't take long to get the board set up (in the hallway at Nerd Towers, so make sure you shout or give a whistle if you're coming out of the dining room!) and to get a couple of games played. There's nothing like hitting two fives and a nine to make you feel like you're actually there, at the Lakeside, competing in the WDO finals. Except...

...what would really make it complete would be a massive, comedy sized scoreboard.
We already had some 4" seven-segment LED displays knocking about. Surely it was just a case of hooking them up to a microcontroller and a MAX7219 driver chip and getting them to display 501?


Knowing nothing about the displayed, we did the usual - poke wires at the terminals until something happened! As it turned out, these displays have four LEDs per segment, not just one, so they actually require about 7.2V to get them to light up.

The process was simply a case of applying power to one of the pins, then grounding others, in turn, until a segment lit up. Because the LEDs are - by their very nature - diodes, it didn't matter if we accidentally put the power lead onto a ground pin; diodes don't let current flow the "wrong way" through them, so nothing happened. After a bit of poking about, we discovered that we had some common anode 7-segment displays.




Having checked the datasheet for the MAX7219 driver, we found it could indeed supply larger voltages than the 5v logic for the data. But these chips are designed to work with common cathode displays, not common anode.

We were going to have to come up with an alternative. Straight away, with anything to do with sinking lots of current, we were thinking about a transistor array. And, luckily, in our lucky bag of ICs, we've a load of ULN2803A chips. They're not just 8-way transistor arrays, but they're ideal for sinking up to 500mA of current - at higher voltages than the logic control signals. Perfect!




But each output requires its own input signal, to hold the output low (grounded).
That means with 3 lots of 8 segments, we'd need no less than 24 i/o pins. That's a lot of pins!

Of course, for a 18F4550 or other PIC with a 40-way package, it's not a problem. Those bad boys have pins to spare. But we've an abundance of 16F1829 chips here, which have a 20-pin count. So we thought that perhaps each ULN2803A current sink could have its own shift register, to allow us to activate as many segments as we liked 





So for each 7-segment display, we've now got a shift register, feeding it's outputs to the "input side" of a ULN2803A. The outputs of the transistor array are connected via 100ohm resistors to the cathodes of the segments.

(not shown in this photo are the resistors between the 7-segment cathods and the transistor array outputs - which is why the display is so bright, running at 9v!)


After a bit of quick etching, and drilling lots of holes with our little dremel...


.... we had a PCB ready for populating

(we may have an abudance of 595 shift registers and 2803A transistor arrays, but there's no point wasting them in case the circuit doesn't work - that's why we're still using through-hole components and sockets for fitting things to the PCB!)

The firmware was completed, allowing characters to be sent to each display, via ASCII. Since our bluetooth modules simply convert bluetooth to serial (at 57600bps) as long as we can correctly parse data over serial, there's no reason why they shouldn't work, parsing data sent from a smartphone app.

Here's an early video of displaying data over serial:



Now we're just waiting on a new bluetooth module (and a free afternoon) to finish the scoreboard and put it in pride of place, over the dartboard!

Friday, 29 January 2016

Apple is shite too

With the recent postings about Windows 10 being so utterly useless, a few people have suggested alternative operating systems. The trouble is:


  • Linux is hard work

Just to get a printer working with it is difficult. It's not that I'm not particularly "tech-savvy". I know how computers work. I just think it's unnecessarily difficult to get a lot of hardware working with Linux. And anyone who says otherwise is usually a little bit sneery about not understanding which command to use at the terminal line, or which sudo apt get package to install.
And the truth is, they don't understand it either - they're just quicker at getting Google to reveal the answer! That's not knowledge, nor understanding. That's just knowing which key words to throw at a search engine to find how someone else did it. That's a useful skill too. But it's not understanding of the system.



  • Apple is evil

Mac computers - on the face of it - look nice. But the all-encompassing power-crazed embrace that Apple put around their technology makes it really difficult to do anything useful with.
Want to hand over some money to install the latest app from the app store? We'll make that easy for you.
Want to hand over some money to listen to music? That's a doddle.
Want to hand over some money to stand in a queue to have your phone looked at by one of our experts because we won't give you the tools to fix things yourself? We'll make that easy for you.
Want to hand over some money to upgrade part of your hardware or toolchain because we don't think hardware should be inter-operable and only Apple should dictate what talks with what? We'll make that easy.
Want to hand over some money to join our developer programme so you can deploy your own software onto Apple devices? We'll make that easy....

Apple makes giving them money easy.
Everything else on a Mac is really difficult.
Try getting some code running on a tablet or phone.
On Android it's easy: create an apk, install on the device. Done!

Don't even try to compile for iOS on a non-Mac device (I tried it. It's just about possible. But really, really difficult). So to target Apple's operating system, you need to use Apple's hardware. And Apple's XCode toolchain. And create certificates. And provisioning profiles. And ensure the provisioning profile matches exactly the one piece of hardware you've identified you want to put the code on. Then piss around some more with a mix of online (web-based) forms plus some local (application-based) settings, download some files (really, why mix online and offline building methods in the same process??!) and finally you might get your code to compile. To run on just one device.

On top of all this, I recently tried to compile a simple Unity app for an iOS device.
It took exactly four minutes on a Windows machine, from launching Unity, to creating an .apk that I could put onto my Android phone. Easy!

I booted up my Mac Mini that was last switched on over a year ago (which was then used to compile some Flash files into an application for iPhone).

  • I had to upgrade the device firmware.
  • Then I had to re-install Unity.
  • Then I had to download and install a massive 4.3Gb complier for iOS.
  • XCode wasn't up-to-date enough for the new compiler, so I had to upate that.
  • But XCode wouldn't update without upgrading the entire operating system.
  • It took nearly two hours to download and re-install Unity and the iOS compiler.
  • It took over two hours to download and install OS X El Capitan.
  • Then I had to re-install XCode. That took nearly three hours.


Having wasted a day, just to get my Mac up to a spec that Apple approved of, I then had to spend about seventy quid renewing my Apple developer licence. Just to make a bouncing ball go up and down on an iPhone screen.

Microsoft might be rubbish. But that's down to their incompetence.
Apple are evil. Because they actually planned this shit.


WTF is wrong with Windows 10?

At the risk of turning this blog into an anti-Microsoft rant, I'm trying to understand why Windows 10 has become so rubbish. Not just "it's a bit like Windows 8 but with naffer graphics". But it's completely, utterly shite.

(Given the experience I had recently with trying to compile a Unity app on a Mac recently, it's unlikely Microsoft will be the only company getting a roasting for producing crappy, needy, simply-fail-to-work-but-offer-no-way-of-going-back-to-a-working-version operating systems)

Both at home, at work and wherever I found a machine running Windows 10, I got rid of it. All except on one machine - a laptop used for testing development on the latest Microsoft operating system. And sometimes, it doesn't make sense to keep flipping between computers; it seems a bit churlish to say "I hate Windows 10 so much, I'm going to put this laptop down and boot up one of my other machines, just to browse the web or download some pdfs".

But it's got to the stage that that's what's happening!
I tried to download a pdf from a website on my laptop.
Some five minutes after it had downloaded, I went into my downloads folder and tried to move it onto the desktop (so it'd be easy to find when I came to read it later)



This was more than five minutes after the file had finished downloading.
A quick look at the task manager and this appeared


Now task manager used to flash a column red if it hit more than 90% or so. It seems like the latest version of Windows 10 now recognises that this is so common, it doesn't do it any more. The stupid Microsoft "anti-malware" is - to me at least - acting like malware.

It's chewing up almost all of the spare CPU and battering the hard disk.
Which in turn renders the rest of the computer useless, as it slows to a crawl. But more than that - it doesn't just make things "a bit slow" or make the computer a "bit cumbersome" but actively stops me from doing what I want with the bleeding thing - which, in this case, was to download a file and put it on the desktop.

If it wasn't for the fact I have to support legacy apps written on a variety of Windows platforms, I think I'd seriously be considering moving to Linux. All of the new development I do now is for mobile platforms (web, Android and - when I absolutely have to - iOS). I used to be a Windows-only developer but it really does look like Windows has had it's day.

I used to build applications with the view that if the mobile version wasn't available, you could always run it on your PC (after all, mobiles don't let you do cool stuff with hardware like plug into the serial port). But more and more it's obvious that it's the PC that's becoming a niche operating system.

I used to think that the proliferation of mobile devices was strangling the computer industry. After all, a mouse is far superior to a pudgy finger for fine resolution and moving things around a screen. And typing on a keyboard is a million times easier than poking a virtual keypad on a screen. Yet consumers (not necessarily computer users, but the majority of people using their tablets to "consume" data) demand less of their devices and simply point at an icon, saying "I want that one".

But it turns out that it's not the demands of the greater masses that's killed desktop computing. It's the developers of the operating systems that have made them so painful to work with. When every action is followed by a dialogue asking for "administrator permissions" or a boot up time of ten minutes while every background service starts and the proceeds to bring the computer to a crawl, it's no wonder people prefer to pick up a tablet and "just go".

But it doing so, OS developers have been going backwards too. In making their operating systems unusable, they're encouraging us to seek out alternative devices and platforms. Ten years ago, the idea of not using Microsoft Office in some shape or form seemed alien to anyone who had ever used a computer (many users confusing Office with Windows and wondering why their PC at home didn't come the full-blown Outlook by default). Now, Office is just another bit of software that you don't need to buy any more.

Windows is slowly becoming the same thing - it's still currently a popular OS. But it's utterly unusable for anything other than the most menial of tasks. It's slow. It doesn't let you do what you want. Half my (admittedly home-built) hardware doesn't work with it. A lot of old software doesn't run on it (unless you remember to right-click and run as administrator). Compilers and development tools don't work nicely together like they used to. In it's current form, Windows is worse than Linux and - dare I say it - probably Mac.

Microsoft used to make awesome software. Sure, it was expensive. And quite bloated. But it worked well. It let you get on with the job - make whatever you needed to make, using software that let you get on and make stuff. But now, Microsoft software is too slow, too bloated and too demanding. It actively gets in the way of trying to get stuff done. And Microsoft don't even make the best/easiest/most useful operating system either.

Interestingly, whenever "big business" appears in the news, it's always Google, Facebook, Amazon, eBay, Apple, even Starbucks that get the press. No-one even thinks to mention Microsoft.

Let's hope Windows 10 dies just as quickly as Microsoft's profile and we can get back to using our technology to do cool stuff, instead of fighting with it on a daily basis!


Wednesday, 27 January 2016

Why is everything - not just iOS - so bloated these days?

Deploying stuff from Unity to Android is a doddle. It's almost drag-n-drop. Create an apk file and hit "build and load" and as if by magic your new app appears on the connected device (provided it's plugged into the USB port, and USB debugging is enabled).

But, in short, it's easy.

Deploying to iOS is a whole different story.
Not least of all because of all the messing about with certificates, provisioning, requests for certificates and all the other junk that goes on. A very good and comprehensive guide to deploying to iOS can be found here - http://gamasutra.com/blogs/ThaleiaDeniozou/20140826/224186/Export_from_Unity_to_an_iOS_Device.php

As we found a few years ago, deploying to an iOS device from anything other than a Mac is real pain. It can be done. We even managed to generate our own developer certificates using a Windows 7 machine, but it involved lots of manual processing and script-bashing to validate Md5-hashes and build files from the ethers.

This time, as we did then, it was just easier to boot up a Mac.
And download the iOS compiler.


As much as we've grown to hate Microsoft (seriously, first .NET, then Windows 10, then the unholy alliance of a .NET Visual Studio hacked onto a Java-based developer-  Unity - running on Windows 10) this just shows that there's no viable alternative.

280Mb vs 4.2Gb?!
I'm not sure that even Apple could justify that amount of bloat....

Monday, 25 January 2016

Microsoft, you broke my laptop. Now you broke Unity!

We were just getting familiar with Unity 5 again recently. And even managed to work out how to use Monodevelop and the debugging and stack trace tools properly. Suddenly developing with C# in Unity wasn't the horrible nightmare it once was.

Then I made the mistake of allowing Unity to upgrade when it booted up this afternoon.
I went from v5.1 to v5.3 and in doing so, bloody Unity went and installed Visual Studio.

Now I'm not suggesting Visual Studio is shit. But it is. In fact, anything running on, developed using or in anyway associated with the (now defunct) .NET framework has always been a bit shit. It's like .NET wasn't actually integrated with Windows, and more sat alongside it.

Let's qualify that - it's probably upset a few people already.
But - anyone who knows me, knows I loved VB6 and Visual Studio (pre .NET). It was great. It created small, tight, easy-to-distribute executables. Applications written in VB6 and VC++6 run well and felt like they were "part of" Windows.

Appplications written with .NET invariably didn't like the particular library version you had on your PC and had to download and install a massive 1.2Gb pile of turgid cack which made half the other applications on the computer break. .NET applications are slow. You can visibly see the screen redrawing (with flexi-grids and data-grids particularly). It wasn't just the crappy mix of the Basic and C++ languages that morphed into C#, the poor use of namespaces or the horrid jumble of unnecessary class setters and getters that C# forced onto you, .NET applications just didn't feel "native" to Windows, even when compiled.

If you run software designed for Linux on a Windows machine, you know the experience. It's ok. But it's not native. The Java runtime (or the JVM virtual machine) it runs on makes it just that little bit glitchy. A bit like when Neo spots the flaws in the Matrix.

A bit like when you  drive a Citroen C2 and pretend it's a Mercedes or a BMW. It does the same thing - it goes, and stops, and burns fuel. But it's not the real thing. There's just something not quite the same about it. It doesn't have the same quality. It feels a bit cheap, when you know you've experienced something better (whereas drivers who've only ever had Citroens, or used Linux exlusively probably have no idea what luxurious means).

For me, .NET applications are like that too.
They do the job. But they're a bit crap, compared to what they should (or could) be.

We already know that Windows 10 is a pile of dog crap. So when Unity, running on my last machine to be running Windows 10, said it was installing Visual Studio, I knew trouble was ahead.

What I can't understand is how, as technology advances and computers supposedly get more powerful, instead of getting better, everything becomes just a little bit more.... well, shit.

When I try to open a file in Unity now, instead of Monodevelop popping up, I get this:


Then after waiting about 20 seconds (in computer terms, that's aaaages) this pops up:


By Microsoft's crappy timing standards, anything they say could take a few minutes is likely to take up to half an hour! As it turns out, I waited nine minutes (I know it took that long as I checked my phone before I left the room, made a brew, went for a sandwich, got back, then received a text - checking the time on the phone again). After nine minutes, nothing had changed.

Except in the taskbar, another application had tried to start and failed.


It wasn't even Visual Studio. It was Monodevelop. Except, somehow, Visual Studio had made it break. Unbelievable! After closing the dialog and making changes to my source code, I flicked back to Unity


The dialog box wouldn't go away.
I had to Task-Manager-End-Task to get it go.
It's broken my install of Unity on my laptop. I don't want Visual Studio on my machine. I'm not even sure I want Windows any more. I can't believe that Microsoft have managed to get things so spectacularly wrong. I even defended Windows 8. But this is a step too far.

It's also been the kick in the pants I needed, to wipe my laptop and go for a fresh factory reset. So I can re-install Windows 7 and Unity 5, and hunt down and uncheck any options to install Visual Studio when I install Unity next time!

Friday, 22 January 2016

Talking to Bluetooth LE devices with Unity 5

In recent months, a few of us have been playing about with Unity. Steve is the master of playing about with stuff. He takes a prefab or shop-bought asset and kicks it about until it just does what he insists it should, through sheer dogged determined willpower. Scropp, like me, takes a slower, more in-depth view of how it works; maybe taking a bit longer to get there, but ultimately having a deeper understanding of how it all works.

That's not to say that either approach is correct, nor that any approach is wrong.
But after playing about with an existing BTLE plug-in from the Asset store(https://www.assetstore.unity3d.com/en/#!/content/26661) I was getting frustrated at being unable to "hack" the existing demo project to do what I wanted it to.


Firstly, it's written in about a million pieces, with classes referencing classes, and references to other Unity scripts (this is a bit of a weird idea for me - where a variable is declared globally, but not actually assigned in code, and simply drag-and-dropped into place using the Unity visual editor).

Secondly, the example script is difficult to follow easily. There are loads of panels and assets that are clearly of no use for most projects (they're included to demonstrate how flexible the plug-in can be with different types of hardware, but you'd never use all of them in the same project). But simply removing assets you don't want causes references to scripts that may or may not be needed to break, and the script stops compiling.

Trying to trace these back and find which reference to which no-longer-needed bit (especially when the reference is placed in the Unity editor and doesn't actually appear in the code anyway) burned a whole day. Sometimes hacking Unity example code can feel a little bit like using Arduino libraries.
With enough perseverance, you can get something to work. But that's about it - really getting it to do what you want instead of having to put up with how it works is about the best you can hope for. I didn't like that idea with my Unity project, so was determined to use the core library for the bluetooth communication, but build my own interface, from a blank Unity Project.

Here's how I did it.

Firstly, import the bluetooth LE library (for iOS and Android - let's build to multiple platforms from the same source code!) Then go into the assets folder and get rid of pretty much most of it!


I kept the plugins folder intact and got rid of everything else. In the screenshot above, the sprites folder remains, but it doesn't actually contain anything! The prefabs folder contains my own prefab (explained later) and the original prefab that came with the demo has been removed. Basically get rid of everything except the contents of the plugins folder.

I created two panels - one shall be the screen when the app first starts, listing all the available bluetooth devices in range, then when one is selected, the app will connect and display the "connected" panel (which is a simple interface for us to send and receive strings of text but you can make yours do pretty much anything you like!)

In the editor they are placed over the top of each other, but in the screenshot have been separated out so you can see the entire hierarchy. I tend to make my panels obviously different colours during development so that you can see which panel is supposed to be showing at any one time.


Unlike the original demo, we're going to shove everything into a single class. It's not exactly perfect programming practice, but this is a single-developer project, not something that's going to be shared as a library, or developed in tandem with fifteen other developers; my personal feeling is that a multi-classed development approach too early on just over-complicates things, when a few simple function calls and simple procedural processing can help make things much easier to understand (which is really useful when you come back to this in six months and don't have to re-learn how it works all over again!)

A controller script is created and attached to the canvas object. For new Unity users, it's as simple as right-clicking in the project and selecting "new script" and giving it a name, then dragging and dropping it onto the canvas object in the hierarchy view on the left. I called my script, rather cryptically, "controller.cs".

In the start function, simply create a couple of global references to the "scanning" and "connected" panels. Maybe it's a throwback to my embedded development time, but I just find global variables much easier to work with that the constant swapping of references and passing parameters that a fully object-oriented approach requires. Don't listen to what your university lecturer tells you - in the real world, we use global variables. A lot.

Then simply initialise the bluetooth plugin with a single line of code:

BluetoothLEHardwareInterface.Initialize (true, false, () => {}, (error) => {});

And then invoke our scanning routine after about one second (which gives the bluetooth plugin time to initialise properly) using

Invoke("scan",1000);

This simply tells Unity to "run the function called -scan- after one second".
The scan function monitors the bluetooth connection and when a device is within range, it calls the AddPeripheral function, to make the device appear in a list, as a clickable (button) object.

And this is where Unity mixes its programmatic and visual approaches. Personally, I don't like it. But some people love it, so this is how it goes:

The AddPeripheral function creates an instance of a button "prefab" and attaches to a (scrollable) panel. But we don't just say "use this panel" but create a global variable in which we store a reference to the scrollable panel object. Strangely, Unity likes this to be set using the visual editor, rather than entirely programmatically. So we create a global variable, so that the editor can access (and amend) the variable and when the script object is viewed in the editor, we can populate all the public variable values.

In fact we had to create a number of global variables to act as references to objects already in our Unity project

// --------------------------------------------------------
// these objects need to be set in the Unity visual editor
// so they are actually linked to something!
// --------------------------------------------------------
public Transform PanelScrollContents; // the panel on which you'll list the devices
public Text txtDebug; // this is just for testing, can be removed safely
public GameObject connectButton; // the button to click to connect to a device
public Text txtData; // the text box to type in your "send" data
public Text txtReceive; // the text box data is received into (for testing)

And then in the visual editor, linked them to the objects on screen



The last thing to do is create our "device connect" button as a prefab, so that it can be created at runtime in code. To create a prefab in Unity is pretty easy. Create your object on the screen in the main editor window and when you're happy with it, drag-n-drop it into the project space (the panel at the bottom in the middle of the screen) and Unity creates a "prefab" version of the object.

Our button has a very simple script attached to it.
It allows us to set the label of the button and also to retrieve the (mac) address of the device to connect to, when the button is pressed.

using UnityEngine;
using UnityEngine.UI;

public class connectButtonScript : MonoBehaviour {

     public controller controllerScript;
     public Text TextName;
     public Text TextAddress;

     public void onClick () {
          controllerScript.connectTo (TextName.text, TextAddress.text);
     }

     // Use this for initialization
     void Start () {
     
     }
     
     // Update is called once per frame
     void Update () {
     
     }
}

Although it's very simple we have to create three global references.
One is a variable onto which we drop the text label on the button. Another is a (transparent/hidden) text label which we use to store the address. And then we have to store a reference to the script/class that has created/called this one. For over twenty years, I've always hated this approach to programming. But here it is. In the AddPeripheral function, you'll see something like this:

GameObject buttonObject = (GameObject)Instantiate (connectButton);
connectButtonScript script = buttonObject.GetComponent ();
script.TextName.text = name;
script.TextAddress.text = address;
script.controllerScript = this;

That's creating an instance of our button prefab, and saying set the .text property of the global variable TextName to whatever the name of the object you've just clicked on is. The global variable TextName appears in the Unity visual editor and you have to drag and drop the "name" label from the button into the TextName global variable. A horrible way of completing references, but one that works, so let's stick with it for now!

Similarly the global variable TextAddress that was declared in the connectButtonScript.cs file is populated in the Unity visual editor by dragging and dropping the "address" label from the button onto the field.

The controllerScript global variable holds a reference to the script/class that created the button (so when the button is clicked, it can make a callback to the script that created it). In our case, a simple "this" will do - the controller.cs class is simply saying to the buttonScript "I am the controller script that created you".

And with all that in mind, here's the full controller.cs script

using UnityEngine;
using UnityEngine.UI;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;

public class controller : MonoBehaviour {

   // -------------------------------------------------------------------
   // this project doesn't offer a "scan" and "connect" buttons - it
   // just gets on and does it!
   // So when it runs, it immediately starts scanning and lists all
   // found devices in a list. Click the one you want and it immediately
   // connects without the need to click a "connect" button
   // -------------------------------------------------------------------

   // -----------------------------------------------------------------
   // change these to match the bluetooth device you're connecting to:
   // -----------------------------------------------------------------
   private string _FullUID = "713d****-503e-4c75-ba94-3148f18d941e";
   private string _serviceUUID = "0000";             
   private string _readCharacteristicUUID = "0002";    
   private string _writeCharacteristicUUID = "0003";

   // ---------------------------------------------------------------------
   // if you want to connect to different devices, you *could* take a look
   // at the function [void connectTo] and add in some if statements to
   // change the FullUID pattern to match the device
   // ---------------------------------------------------------------------



   // ----------------------------------------------------------------------
   // the following are public because they need to be set by dragging and
   // dropping the various objects in the Unity editor
   // ----------------------------------------------------------------------
   // --------------------------------------------------------
   // these objects need to be set in the Unity visual editor
   // so they are actually linked to something!
   // --------------------------------------------------------
   public Transform PanelScrollContents;            // the panel on which you'll list the devices
   public Text txtDebug;                        // this is just for testing, can be removed safely
   public GameObject connectButton;               // the button to click to connect to a device
   public Text txtData;                        // the text box to type in your "send" data
   public Text txtReceive;                        // the text box data is received into (for testing)


   // -------------------------------------------------------------
   // leave the rest of this junk alone but edit the couple
   // of functions that send and receive data if you like:
   //
   // sendDataBluetooth('string to send');
   //
   // and when data is received, it's passed into the function:
   //
   // receiveText('string received over bluetooth');
   //
   // so change these functions to do whatever you want them to do
   // and leave all the other stuff alone and it should just work!
   // -------------------------------------------------------------

   public bool isConnected=false;
   private bool _readFound=false;
   private bool _writeFound=false;
   private string _connectedID = null;

   private Dictionary<string, string> _peripheralList;
   private float _subscribingTimeout = 0f;
   private bool _scanning = false;
   private bool _connecting = false;

   private int devicesFound = 0;

   private GameObject panelScan;
   private GameObject panelConnected;
   private GameObject panelSettings;
   

   void connectBluetooth(string addr){
      BluetoothLEHardwareInterface.ConnectToPeripheral (addr, (address) => {
      },
      (address, serviceUUID) => {
      },
      (address, serviceUUID, characteristicUUID) => {
      
         // discovered characteristic
         if (IsEqual (serviceUUID, _serviceUUID)) {
            _connectedID = address;         
            isConnected = true;
         
            if (IsEqual (characteristicUUID, _readCharacteristicUUID)) {
               _readFound = true;
            } else if (IsEqual (characteristicUUID, _writeCharacteristicUUID)) {
               _writeFound = true;
            }

            showConnected();
         }
      }, (address) => {
      
         // this will get called when the device disconnects
         // be aware that this will also get called when the disconnect
         // is called above. both methods get call for the same action
         // this is for backwards compatibility
         isConnected = false;
      });

      _connecting = false;
   }
   
   string FullUUID (string uuid) {
      // this has changed for the BTLE Mini devices
      // return "713d" + uuid + "-503e-4c75-ba94-3148f18d941e";
      return _FullUID.Replace ("****", uuid);
   }
   
   bool IsEqual(string uuid1, string uuid2){
      if (uuid1.Length == 4) {
         uuid1 = FullUUID (uuid1);
      }
      if (uuid2.Length == 4) {
         uuid2 = FullUUID (uuid2);
      }
      return (uuid1.ToUpper().CompareTo(uuid2.ToUpper()) == 0);
   }

   public void connectTo(string sName, string sAddress){
      if (_connecting == false) {
         txtDebug.text += "Connect to " + sName + " " + sAddress + "\n";
         _connecting=true;

         // stop scanning
         BluetoothLEHardwareInterface.StopScan ();
         _scanning = false;

         // connect to selected device
         connectBluetooth (sAddress);
      }
   }

   public void scan(){
      if (_scanning==true) {
         txtDebug.text+="Stop scan\n";
         BluetoothLEHardwareInterface.StopScan ();
         _scanning = false;
      } else {

         txtDebug.text+="Start scan\n";
         RemovePeripherals ();
            
         devicesFound=0;

         // the first callback will only get called the first time this device is seen
         // this is because it gets added to a list in the BluetoothDeviceScript
         // after that only the second callback will get called and only if there is
         // advertising data available
         BluetoothLEHardwareInterface.ScanForPeripheralsWithServices (null, (address, name) => {               
            AddPeripheral (name, address);               
         }, (address, name, rssi, advertisingInfo) => {});
            
         _scanning = true;
      }         
   }


   void RemovePeripherals () {
      for (int i = 0; i < PanelScrollContents.childCount; ++i) {
         GameObject gameObject = PanelScrollContents.GetChild (i).gameObject;
         Destroy (gameObject);
      }
      
      if (_peripheralList != null) {
         _peripheralList.Clear ();
      }
   }
   
   void AddPeripheral (string name, string address){
      if (_peripheralList == null) {
         _peripheralList = new Dictionary<string, string> ();
      }
      if (!_peripheralList.ContainsKey (address)) {

         txtDebug.text+="Found "+name+"\n";
         devicesFound++;

         GameObject buttonObject = (GameObject)Instantiate (connectButton);
         connectButtonScript script = buttonObject.GetComponent<connectButtonScript> ();
         script.TextName.text = name;
         script.TextAddress.text = address;
         script.controllerScript = this;

         // each button is 50 pixels high
         // the container panel is 544 pixels high
         var h = (544 / 2) - (55*devicesFound);

         buttonObject.transform.SetParent (PanelScrollContents);
         buttonObject.transform.localScale = new Vector3 (1f, 1f, 1f);   
         buttonObject.transform.localPosition = new Vector3 (0, h, 0);

         _peripheralList[address] = name;

         txtDebug.text+="Button created\n";
      }
   }

   
   void sendByte (byte value) {
      byte[] data = new byte[] { value };
      BluetoothLEHardwareInterface.WriteCharacteristic (_connectedID, FullUUID (_serviceUUID), FullUUID (_writeCharacteristicUUID), data, data.Length, true, (characteristicUUID) => {         
         BluetoothLEHardwareInterface.Log ("Write Succeeded");
      });
   }
   
   void sendBytesBluetooth (byte[] data) {
      BluetoothLEHardwareInterface.Log (string.Format ("data length: {0} uuid: {1}", data.Length.ToString (), FullUUID (_writeCharacteristicUUID)));
      BluetoothLEHardwareInterface.WriteCharacteristic (_connectedID, FullUUID (_serviceUUID), FullUUID (_writeCharacteristicUUID), data, data.Length, true, (characteristicUUID) => {         
         BluetoothLEHardwareInterface.Log ("Write Succeeded");
      });
   }

   void sendDataBluetooth(string sData){
      if (sData.Length > 0) {
         byte[] bytes = ASCIIEncoding.UTF8.GetBytes (sData);
         if (bytes.Length > 0) {
            sendBytesBluetooth (bytes);
         }      
      }
   }

   void receiveText(string s){
      txtReceive.text += s;
   }

   public void clearReceived(){
      txtReceive.text = "";
   }

   public void sendBluetooth(){
      string sData = txtData.text;
      txtDebug.text+="Sending: "+sData+"\n";
      sendDataBluetooth (sData);
      txtDebug.text += "Sent";
   }

   void disconnect (Action<string> action){
      BluetoothLEHardwareInterface.DisconnectPeripheral (_connectedID, action);
   }

   void showScan(){
      panelSettings.SetActive (false);
      panelConnected.SetActive (false);
      panelScan.SetActive (true);
   }

   void showConnected(){
      panelSettings.SetActive (false);
      panelScan.SetActive (false);
      panelConnected.SetActive (true);
   }

   void showSettings(){
      panelScan.SetActive (false);
      panelConnected.SetActive (false);
      panelSettings.SetActive (true);
   }

   void Initialise(){
      BluetoothLEHardwareInterface.Initialize (true, false, () => {}, (error) => {});
   }
   
   // Use this for initialization
   void Start () {

      panelScan = GameObject.Find ("panelScan");
      panelSettings = GameObject.Find ("panelSettings");
      panelConnected = GameObject.Find ("panelConnected");

      // set up the panels
      showScan ();

      // initialise the bluetooth library
      Initialise ();

      // start scanning after 1 second
      Invoke("scan",1000);
   }



   // Update is called once per frame
   void Update () {
   
      if (_readFound && _writeFound){
         _readFound = false;
         _writeFound = false;         
         _subscribingTimeout = 1f;
      }
      
      if (_subscribingTimeout > 0f){
         _subscribingTimeout -= Time.deltaTime;
         if (_subscribingTimeout <= 0f){
            _subscribingTimeout = 0f;
            
            BluetoothLEHardwareInterface.SubscribeCharacteristicWithDeviceAddress (_connectedID, FullUUID (_serviceUUID), FullUUID (_readCharacteristicUUID), (deviceAddress, notification) => {               
            }, (deviceAddress2, characteristic, data) => {
               
               BluetoothLEHardwareInterface.Log ("id: " + _connectedID);
               if (deviceAddress2.CompareTo (_connectedID) == 0)
               {
                  BluetoothLEHardwareInterface.Log (string.Format ("data length: {0}", data.Length));
                  if (data.Length == 0){
                     // do nothing
                  } else {
                     string s = ASCIIEncoding.UTF8.GetString (data);
                     BluetoothLEHardwareInterface.Log ("data: " + s);
                     receiveText(s);
                  }
               }
               
            });

         }

      }

   }
}