2. Light Detector Tutorial


This code will automatically detect your Bit:Bot model using the I2C bus, No need to change the code when swapping between models.

Your Bit:Bot has 2 tiny light sensors on the front of either side. We will use these sensors in a later project to create a light avoiding and light seeking robot but before we do that we need to have a look at how these sensors work.

The Micro:Bit has 2 types of input/output pins digital and analogue; the digital pins are either a 1 or a 0, on or off and the analogue pins can have 1024 different values 0 – 1023. These values are represented by a 10 bit binary number in the Micro:Bits memory. The light sensors will alter the voltage going to some of the analogue pins depending on how much light they’re detecting; the more light, the greater the voltage and the greater the number detected by the analogue pins.

In this example we will take a reading from the light sensors and alter the brightness of the LEDs depending on the brightness of the detected light from the light sensors.

Open Mu and click the new button to start a new project. Then open the last project we did with the NeoPixels and copy and paste it into your new project. Then delete everything below the program loop. That will be everything below the while True: statement. Once that’s done delete the comments at the top of the screen and write something more appropriate. Or if that sounds like hard work just copy and paste the code below into your new project.

[pastacode lang=”python” manual=”%23%20Light%20sensor%20example%20for%20the%204tronix%20Bit%3ABot%20and%20BBC%20Micro%3ABit%0A%23%20Author%20David%20Bradshaw%202017%0A%23%20Demonstrates%20how%20to%20use%20the%20light%20sensors%0A%0Afrom%20microbit%20import%20*%0Aimport%20neopixel%20%20%23%20Neopixel%20Library%20so%20we%20can%20control%20the%20NeoPixels%20lights%0A%0Anp%20%3D%20neopixel.NeoPixel(pin13%2C%2012)%0A%0A%0Adef%20leftLights(Red%2C%20Green%2C%20Blue)%3A%0A%20%20%20%20for%20pixel_id%20in%20range(0%2C%206)%3A%0A%20%20%20%20%20%20%20%20np%5Bpixel_id%5D%20%3D%20(Red%2C%20Green%2C%20Blue)%0A%20%20%20%20np.show()%0A%0A%0Adef%20rightLights(Red%2C%20Green%2C%20Blue)%3A%0A%20%20%20%20for%20pixel_id%20in%20range(6%2C%2012)%3A%0A%20%20%20%20%20%20%20%20np%5Bpixel_id%5D%20%3D%20(Red%2C%20Green%2C%20Blue)%0A%20%20%20%20np.show()%0A%0A%0Awhile%20True%3A” message=”Import Some Libs and Create 2 Functions to Light the LEDs” highlight=”” provider=”manual”/]

Now save the new project with a file name of your choice. Be careful not to overwrite the old NeoPixel example. If you do and require the code at a later date you can always go to the Resources page and download the code you need.

Now we will assign the analogue pins that the light detectors are connected to. You can have a look at the bottom of your Bit:Bot to see what these pins are. Type the below code underneath the line np = neopixel.NeoPixel(pin13, 12)

[pastacode lang=”python” manual=”%23%20For%20Bit%3ABot%20Classic%0A%23%20Both%20light%20sensor%20are%20on%20the%20same%20pin%20so%20we%20also%20use%20a%20select%20pin%0AlightSensor%20%3D%20pin2%0AsensorSelect%20%3D%20pin16%0A%0A%23%20For%20Bit%3ABot%20XL%0AleftLightSensor%20%3D%20pin2%0ArightLightSensor%20%3D%20pin1″ message=”Assign the Light Sensor Pins” highlight=”” provider=”manual”/]

Bit:Bot Classic: We have assigned pin 2 for the light sensors and pin 16 for sensor select. When 4Tronix deigned Bit:Bot they decided to connect both sensors to the same pin and have another pin as a pin select. Although we have only assigned one pin for the light sensor we have 2 light sensors connected to that pin.

Bit:Bot XL: We assigned pin 1 to the right sensor and pin 2 to the left senor, to get these values we will use analog read.

Now we’re going to create a method that will read the values from each light sensor and set the brightness of the NeoPixels depending on the brightness of the detected light. under the rightLights() method make sure that there’s 2 blank lines with no white space and type;

[pastacode lang=”python” manual=”def%20lightSense(model)%3A%0A%09if%20model%20%3D%3D%20%22classic%22%3A%0A%09%09sensorSelect.write_digital(0)%0A%09%09brightness%20%3D%20lightSensor.read_analog()%0A%09%09brightness%20%3D%20int(brightness%20%2F%20100)%0A%09%09leftLights(brightness%2C%200%2C%200)%0A%09%0A%09%09sensorSelect.write_digital(1)%0A%09%09brightness%20%3D%20lightSensor.read_analog()%0A%09%09brightness%20%3D%20int(brightness%20%2F%20100)%0A%09%09rightLights(brightness%2C%200%2C%200)%0A%09%0A%09elif%20model%20%3D%3D%20%22XL%22%0A%09%09brightness%20%3D%20leftLightSensor.read_analog()%0A%09%09brightness%20%3D%20int(brightness%20%2F%20100)%0A%09%09leftLights(brightness%2C%200%2C%200)%0A%09%0A%09%09brightness%20%3D%20rightLightSensor.read_analog()%0A%09%09brightness%20%3D%20int(brightness%20%2F%20100)%0A%09%09rightLights(brightness%2C%200%2C%200)%0A%0A%0Awhile%20True%3A%0A%20%20%20%20lightSense(%22classic%22)%20%23%20use%20this%20for%20Classic%2C%20use%20lightSense(%22XL%22)%20for%20XL” message=”Get Some Readings from the Light Sensors” highlight=”” provider=”manual”/]

The above code works the same for Classic and XL versions of Bit:Bot, the only difference is that the classic version uses a senor select pin.

I’ve introduced a few new things in the above code so I will explain what it is doing in plain English.

  1. We select sensor 0 which is the left sensor
  2. We then assign sensor 0’s value (via the read_analog() function) to an integer called brightness
  3. We then divide this number by 100 and ensure that the result is an integer value
  4. we then assign that value to the left LEDs (red colour component only) on Bit:Bot via the method leftLights()
  5. Above steps are then repeated for the right sensor

Here we use write_digital() to write a value to a digital pin and read_analog() to read an analogue value from the sensors. We then divided this number by 100 and use a technique called casting to ensure that the resulting value is an integer.

Integers are whole numbers i.e 1, 10, 22, 1547, etc anything that is not a whole number i.e. 1.24, 5.5, 154.25 can not be stored in an integer variable, if we didn’t cast the value to an int Bit:Bot would have given us an error. This is because we’re dividing brightness by a number; the result of which could be a decimal number that can’t be stored as an integer value. By casting what we have done is to round up this number to the nearest whole number so it can be stored as an integer value (and not crash the Micro:Bit). How decimal numbers or floating point numbers as we call them in the computing world are stored is a subject for another day. If your interested in this I suggest you look into data types, binary number system, floating point numbers and 2’s complement.

Maybe your wondering why I even divided the brightness in the first place; this is because the maximum number you can give to the method leftLights() is 255. This is due to it using an 8 bit binary number to store each colour components brightness. 2^8 is 256 which gives us the number range of 0 – 255. If we gave that method a number larger than 255 we would get an overflow error and the Micro:Bit would crash. The analogue pins have a precision of 2^10 this means that it can have 1024 different values 0 – 1023. In order to avoid the overflow error we need to divide brightness by at least 4 in order to ensure that it will always be below 256. I decided to divide it by 100 to keep the brightness of the LEDs down so it doesn’t hurt your eyes or give you a headache 🙂 .

The completed code for this exercise is below;

[pastacode lang=”python” manual=”%23%20Light%20sensor%20example%20for%20the%204tronix%20Bit%3ABot%20and%20BBC%20Micro%3ABit%0A%23%20Author%20David%20Bradshaw%202017%0A%23%20Demonstrates%20how%20to%20use%20the%20light%20sensors%0A%0Afrom%20microbit%20import%20*%0Aimport%20neopixel%20%20%23%20Neopixel%20Library%20so%20we%20can%20control%20the%20NeoPixels%20lights%0A%0Anp%20%3D%20neopixel.NeoPixel(pin13%2C%2012)%0A%0A%23%20For%20Bit%3ABot%20Classic%0A%23%20Both%20light%20sensor%20are%20on%20the%20same%20pin%20so%20we%20also%20use%20a%20select%20pin%0AlightSensor%20%3D%20pin2%0AsensorSelect%20%3D%20pin16%0A%0A%23%20For%20Bit%3ABot%20XL%0AleftLightSensor%20%3D%20pin2%0ArightLightSensor%20%3D%20pin1%0A%0Adef%20detectModel()%3A%20%20%23%20Detects%20which%20model%20were%20using%20XL%20or%20classic%0A%20%20%20%20global%20robotType%0A%20%20%20%20try%3A%0A%20%20%20%20%20%20%20%20value%20%3D%20i2c.read(28%2C%201%2C%20repeat%3DFalse)%20%20%23%20Read%20i2c%20bus%0A%20%20%20%20%20%20%20%20robotType%20%3D%20%22XL%22%20%20%23%20If%20we%20can%20read%20it%20then%20it%20must%20be%20XL%0A%20%20%20%20%20%20%20%20display.show(%22X%22)%0A%20%20%20%20except%3A%0A%20%20%20%20%20%20%20%20robotType%20%3D%20%22classic%22%20%20%23%20If%20we%20can’t%20read%20it%20it%20must%20be%20classic%0A%20%20%20%20%20%20%20%20display.show(%22C%22)%20%20%20%20%20%20%23%20or%20Micro%3Abit%20is%20unplugged%0A%20%20%20%20sleep(1000)%20%20%23%20Do%20this%20so%20the%20user%20can%20see%20if%20the%20correct%20model%20is%20found%0A%0A%0Adef%20leftLights(Red%2C%20Green%2C%20Blue)%3A%0A%20%20%20%20for%20pixel_id%20in%20range(0%2C%206)%3A%0A%20%20%20%20%20%20%20%20np%5Bpixel_id%5D%20%3D%20(Red%2C%20Green%2C%20Blue)%0A%20%20%20%20np.show()%0A%0A%0Adef%20rightLights(Red%2C%20Green%2C%20Blue)%3A%0A%20%20%20%20for%20pixel_id%20in%20range(6%2C%2012)%3A%0A%20%20%20%20%20%20%20%20np%5Bpixel_id%5D%20%3D%20(Red%2C%20Green%2C%20Blue)%0A%20%20%20%20np.show()%0A%0A%0Adef%20lightSense(model)%3A%0A%09if%20model%20%3D%3D%20%22classic%22%3A%0A%09%09sensorSelect.write_digital(0)%0A%09%09brightness%20%3D%20lightSensor.read_analog()%0A%09%09brightness%20%3D%20int(brightness%20%2F%20100)%0A%09%09leftLights(brightness%2C%200%2C%200)%0A%09%0A%09%09sensorSelect.write_digital(1)%0A%09%09brightness%20%3D%20lightSensor.read_analog()%0A%09%09brightness%20%3D%20int(brightness%20%2F%20100)%0A%09%09rightLights(brightness%2C%200%2C%200)%0A%09%0A%09else%3A%20%20%23%20XL%20model%0A%09%09brightness%20%3D%20leftLightSensor.read_analog()%0A%09%09brightness%20%3D%20int(brightness%20%2F%2050)%0A%09%09leftLights(brightness%2C%200%2C%200)%0A%09%0A%09%09brightness%20%3D%20rightLightSensor.read_analog()%0A%20%20%20%20%20%20%20%20brightness%20%3D%20int(brightness%20%2F%2050)%0A%20%20%20%20%20%20%20%20rightLights(brightness%2C%200%2C%200)%0A%0AdetectModel()%0A%0Awhile%20True%3A%0A%20%20%20%20lightSense(robotType)%20%0A” message=”Completed Code” highlight=”” provider=”manual”/]

Check your code with the check button and Flash it to your Micro:Bit. Cover up the sensors and watch the LEDs get brighter and dimmer as you do it. The sensors are on the ends of the arms where the LEDs are.

Please Note: The XL uses different less sensitive light detectors and because of this you might need a bright light source to illuminate the LEDs such as a torch.

Now we’re going to change the lightSense() method so it will return a brightness value rather than setting the brightness. It will also allow us to set the minimum brightness.

[pastacode lang=”python” manual=”def%20setBrightness(minValue%2C%20model)%3A%0A%09if%20model%20%3D%3D%20%22classic%22%3A%0A%20%20%20%20%09sensorSelect.write_digital(0)%0A%20%20%20%20%09brightnessLeft%20%3D%20lightSensor.read_analog()%0A%20%20%20%20%09sensorSelect.write_digital(1)%0A%20%20%20%09%09brightnessRight%20%3D%20lightSensor.read_analog()%0A%09%09%0A%09elif%20model%20%3D%3D%20%22XL%22%3A%20%20%20%20%09%0A%20%20%20%20%09brightnessLeft%20%3D%20leftLightSensor.read_analog()%0A%20%20%20%09%09brightnessRight%20%3D%20rightLightSensor.read_analog()%0A%09%09%0A%0A%20%20%20%20brightness%20%3D%20int((brightnessLeft%20%2B%20brightnessRight)%20%2F%202)%0A%20%20%20%20brightness%20%3D%20int(brightness%20%2F%2025)%0A%20%20%20%20if(brightness%20%3C%20minValue)%3A%0A%20%20%20%20%20%20%20%20brightness%20%3D%20minValue%0A%20%20%20%20return%20brightness” message=”Lets Tidy up the Code” highlight=”” provider=”manual”/]

The above setBrightness() method returns a value depending on ambient light levels. This method will be used in the next example; I have used if statements and return values don’t worry about them we will cover it all in the next example when we play with the IR detectors. Look at the code and try to figure out what its doing, what’s the if statement for and why do I add both brightness values together and divide by 2? put your thoughts in the comments section.

This code doesn’t do anything exciting however we have covered some important points. Building on what we went through in the previous exercise then reading from an analogue pin, writing to a digital pin, assigning pins and casting to ensure that results of a calculation is in a usable format. The setBrightness() method will be used for all examples that utilise the LEDs to ensure that they are not too bright.

Well done for completing this example we can now light up the LEDs and use the light sensors to detect light. When you feel confident to move on go to the next tutorial that will look at how to use the IR detectors on the bottom of the Bit:Bot to detect black lines so we can create a line following robot.

Code Files

The below zip file contains the above code and a few extra bits


For the code to work unzip the file, open in Mu and upload to the Micro:Bit.