We are very pleased to have the community participation in content development and share. Philip Le Riche has created a series of lessons which a group of Year 8 (12-13 year old) girls are currently working through. You can reach him via twitter @pleriche

You can download all the content as a single zip file from Philip Github Repo

Lightino Finger Spinner

Introduction

Lightino is a nifty fidget spinner with eight 3-color LEDS that you can program to display any patterns you like. You can even make it spell out messages as it spins. It also contains an infrared sensor that allows it to sense the start of each rotation so that you can make the pattern or the message always start in the same place.

Take a look at the spinner. The 3 arms contain the 8 LEDs, a lithium-ion battery and an Arduino. There’s an on/off switch between the battery and the Arduino. Slide the tiny knob with your finger nail.

lightinoParts

The Arduino is the “brains”. You can program it through the micro USB port on the end. To write your program (called a “sketch” in Arduino terms) and upload it you need to run the Arduino IDE (IDE stands for Integrated Development Environment) which runs on a PC.

You will be programming in the “C” language. This is very powerful, but even so, it’s simple to do simple things. You can find a brief introduction and survival guide in an appendix to this lesson set.

Lesson 0: How to program the Lightino

Getting started

If you want to install the IDE on a computer at home you can find the instructions at:
https://coolcomponents.co.uk/blogs/news/getting-started-with-the-lightino-fidget-spinner

On the Arduino IDE Tools menu, select Board: and then Arduino Leonado. This is important!

Your first program

The first program (or “sketch”) anyone ever runs on an Arduino is called Blink. It simply flashes a LED on and off.

Lightino Blink
  Turns on an LED on and off for half a second each repeatedly.
 */
// Arduino pin defines
#define RED   A1
#define GREEN A0
#define BLUE  A2
#define LED1 2
#define LED2 3
#define LED3 4
#define LED4 5
#define LED5 6
#define LED6 7
#define LED7 8
#define LED8 9

int led_anodes[] = {LED1, LED2, LED3, LED4, LED5, LED6, LED7, LED8};
int led_cathodes[] = {RED, GREEN, BLUE};

// The setup function runs once when you press reset or power the board
void setup() {
  pinMode(LED1, OUTPUT);
  pinMode(GREEN, OUTPUT);
  digitalWrite(GREEN, HIGH);
}

// The loop function runs over and over again forever
void loop() {
  digitalWrite(LED1, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(500);              // wait for half a second
  digitalWrite(LED1, LOW);    // turn the LED off by making the voltage LOW
  delay(500);              // wait for half a second
}
<span id="mce_SELREST_start" style="overflow:hidden;line-height:0;"></span>

Here it is. Copy and paste it into the Arduino IDE, replacing the skeleton sketch it gives you to start with. (Click File – New from the menu if you need to create a new sketch.) Save it with name Blink.

Understanding it

The Arduino has a number of programmable input/output pins which in our case are connected to the LEDs. So the first thing in the sketch is to define names for the pins with the #define statements.

Every Arduino sketch has two functions: the first is setup(). This is called once at the beginning and is used to set up things like which pins are to be inputs and which outputs.

The other function is loop() which is then invoked repeatedly for ever.

Launch Windows Explorer (a quick way is to press the Windows key and “E”). In My Documents you should find a folder called Arduino, and in it a folder called Blink. Within that folder is your actual sketch, a file called Blink.ino. Every sketch has its own folder because some sketches consist of more than one file.

Instead of pasting code into a new sketch in the IDE you can create a folder with the name of your sketch in your Arduino folder, then copy the sketch file (with suffix .ino) into that new folder. The IDE will then find it if you click File – Sketchbook.

On the Arduino Sketch menu, select Verify/Compile. This will check your sketch for syntax errors then compile it (i.e. translate it into binary code that the chip understands).

If you don’t get any errors, plug the Lightino into a USB port, and on the Sketch menu select Upload (not Upload using Programmer).

It will complete the compilation, then you should see it scanning COM ports for the Lightino. At this point, quickly press the reset button on the Lightino. You should now see it writing the sketch to the Lightino, then reading it back to check, during each of these, displaying # characters to show progress.

Your sketch should now run! Unplug the USB lead and switch on the switch to make it run from its own battery.

Lesson 1: Let’s make patterns!

How to drive the LEDs

We’re now ready to start the fun! But first we need to understand how the LEDs work.

Each of the 8 LEDs is actually 3 LEDs in one package: a red, a green and a blue. Each has 2 connections: the anode (the positive one) and the cathode (the negative). All the red cathodes are connected together and all the green cathodes and all the blue cathodes. All 3 anodes in each package are connected together.

lightinoleds

You can activate either the red or the green or the blue cathodes by writing something like digitalWrite(RED, HIGH). You can then activate whichever anodes you like by things like digitalWrite(LED1, HIGH). Only when you’ve done both will anything light up.

Your new sketch

In your Arduino folder, create a new folder called Pattern1. Copy into it the file Pattern1.ino from the zip file in the introduction.

In the Arduino IDE, click File – Sketchbook. You should see your new sketch Pattern1 which you can open.

Take a look at the function loop(). We saw that this is called repeatedly for ever, but within it are 3 more loops. We can summarise them in “pseudocode” (pretend computer code just to help us understand what’s going on) as follows:

Do 20 times:

Do for each colour:

 Switch on the colour (cathode)

 Do 8 times:

   Turn on the next LED then wait a bit

 Turn off all the LEDs

 Turn off the colour.

Then there’s another set of 3 loops doing something similar, but this time it draws diamonds. Can you see how it does it?

Now over to you! Can you change it to make hollow diamonds?

Lesson 2: Let’s make hearts!

In the last lesson we used code to decide which LEDs to turn on or off to make a pattern. But what if we want to make it show a pattern such as a heart? If we draw the pattern out on squared paper we can take vertical slices, see which LEDs are to be lit for each slice, and tell the Arduino to light them in turn.

Here’s the pattern I drew but you can change it if you think you can draw one you like better, or you could do a smiley face or anything else you like. It worked best for me with 7 columns plus a blank one to turn the LEDs off.

LED1
LED2
LED3
LED4
LED5
LED6
LED7
LED8

The simplest way to do this is to code each column as a binary number, a 1 representing a LED on and a 0 for a LED off. (In C we write binary numbers starting “0b”, so 3 in binary would be “0b11”.)

So the first column will be 0b00011110, and the second will be 0b00100001. (We count the bits in a binary number from right to left, starting at zero, so bit 0, representing LED1, is 0 in the first column and 1 in the second.) Check you can see how I got that.

We store these binary numbers in an array called heart, which is simply a list of values. We declare it as having 8 elements and pre-set them like this:

int heart[8] = { 0b00011110, 0b00100001, … }

(We can put this before setup() after the other definitions, because we don’t want it redefined every time loop() gets called.)

We can then refer to the first element as heart[0], the second as heart[1] and so on.

There’s a function bitRead which extracts bit n (counting from zero at the right hand end) from a binary number. So bitRead(heart[0]) is 0, and bitRead(heart[1]) is 1.

Now HIGH and LOW as we usually write in digitalWrite are actually the same as 1 and 0, so we can directly use bitRead in a digitalWrite!

We’re now ready to write our code. Let’s display 3 hearts in red, green and blue. Here is the pseudocode:

For each colour:

Switch on the colour (cathode)

For each column 0 - 7:

 For each row 0 - 7:

  Turn the LED on or off according to the bit (row)

   of the array element (column)

 Wait a bit

Take the previous example (Pattern1), save it as Pattern2 and replace the code in loop() with your own. Now see if you can write the code.

Lesson 3: Hold it! Stop your pattern from spinning!

You will have noticed that the patterns you’ve made so far spin round, because we haven’t told the Lightino when to start during a rotation. This is what the sensor is for. We write to the LEDs with a digitalWrite() function, so you could probably guess that we read from the sensor with digitalRead(). This normally returns HIGH, but returns LOW if it sees something passing over the sensor.

Good interruptions!

In our sketch, we could simply wait for the sensor to go LOW before starting the pattern, but there’s a better method using “interrupts”. Let me explain.

At any time on your PC you might hit a key on the keyboard, or move the mouse, or some data might arrive from the hard disk or the Internet. If it had to spend all its time watching for those things it’d never get anything else done! Instead, the hardware detects these events and when one of them happens it causes the processor to save where it’s got to and what it’s doing, execute a small piece of code called an Interrupt Service Routine (or ISR) to save away the incoming data and make a note to itself that there’s some data to process when it’s got a moment, then resume what it was doing.

The Arduino IDE provides functions for managing interrupts, but unfortunately not on the input that the Lightino sensor is connected to. So we have to rely on one or two magic incantations invented by someone who knows a lot more about the processor chip than even I do!

In your sketch…

Right at the beginning of the sketch we need to include the following line:

#include <avr/pgmspace.h>

This calls up definitions of the terms used in the magic incantations.

Somewhere before setup() we will need to declare some variables. These are only the sort of magic you’re already used to.

// Sensor timings in microseconds
unsigned long curTime;   // Most recent sensor detection

unsigned long lastTime;   // Previous sensor detection time

unsigned long revTime;   // Time for last full rotation

unsigned long dwellTime; // Time between LED changes (based on rotation speed)

int dwellDegrees = 4;     // Degrees of rotation between pixels

int revolutions;         // Total number of revolutions, in case we want to know

boolean flag = false;     // Set by the ISR

boolean overrun = false; // Set if we didn't respond to flag being set soon enough

Some of these are declared as type unsigned long, which means they’re stored as 32 bits rather than 16 bits, so allowing values up to around 4 billion. We know they’ll never be negative so we don’t need to waste a bit to say they’re not.

Then in setup() we need to include these lines of deep magic:

// Interrupt setting
PCMSK0 = bit (PCINT6); //Use PCINT6 = PB6

PCICR |= (1<<PCIE0);   //Enable Pin change interrupt

sei();

This tells the chip to raise an interrupt whenever a change is detected on pin 10 (known to the chip as PB6), to which the sensor is connected.

More magic goes at the end of the sketch:

ISR(PCINT0_vect){ // Sensor input change detected

if (!digitalRead(SENSOR)){

   overrun = flag; // If flag is still set from last time, indicate an over-run

   flag = true;

   revolutions += 1;

}

}

This is the interrupt service routine, called whenever an interrupt is raised by a change in the sensor output. If the sensor now reads false it sets a flag for the code in loop() to act on, but first sets the boolean overrun if loop() hadn’t got around to resetting the flag yet.

At the start of loop() we need to add:

if (overrun) flag = false; // Last rotation over-ran? Wait for next

if (flag){

   flag=false;

   curTime = micros();

   revTime = curTime - lastTime;

   lastTime = curTime;

   if (revTime < 20000 || revTime > 300000) revTime = 25000;

   dwellTime = (revTime * 3 / 360);

The function micros() returns the number of microseconds (millionths of a second) since the chip was the last reset. This can be a big number, which is why we had to declare the variables we use here as type long.

First, we see if overrun is set, in which case we’d better wait for another revolution and for the flag to be set again.

Then we wait for the flag to be set (remember, loop() is called repeatedly). We record the current time in microseconds and subtract the time last time we came here to get the time of a revolution. We check it’s a sensible number, which it won’t be on the first revolution. Finally, we calculate how long a LED must be lit to draw a single pixel, which we choose to be the time taken to spin through an angle of 3⁰.

Put it all together, compile it and upload it, and see if your hearts now stop spinning!

Lesson 4: Let’s display messages!

You can display messages just as well as patterns since each letter, digit or symbol is just a pattern. Luckily for us, someone has already worked out all the patterns we need and a function for displaying them!

Save your previous sketch (Interrupt) with a new name “Text”. We can keep most of it.

We need a new file font.h containing the patters for the text characters. Copy it in the folder Text.

Include it in your sketch by adding the following line at the top:

#include "font.h"

You also need 2 new functions displayChar() and displayCharLower() which you can find in this fil <*>. Copy and paste them into your sketch after the end of the function loop().

The function displayChar() displays a character, and displayCharLower() does the same except the other way up and backward. You can use it to display text the right way up along the bottom of the circle as the LEDs spin.

And now to code!

Let’s may it display “Hello” on the top and “Year8!” on the bottom.

Insert 2 lines somewhere before setup() as follows:

int rows= 8;           // Total LED's in a row

const int charHeight = 8;

const int charWidth = 5;




char textString_Up[] = "Hello";

char textString_Down[] = "Year8!";

(You can change the message if you like but that’s about as long as you can make it.)

Now you have to change the code in loop(), replacing the 2 for loops beginning

for (int c =

with your new code. (Make sure your curly brackets still match up.)

First of all, switch on one of the colors with a digitalWrite() call. (Can you remember how to do that?) Now loop through the first string displaying the characters. This is how to do it:

for (int k=0; k<strlen(textString_Up); k++){

displayChar(textString_Up[k]);

}

Can you see how that works?

Before starting the other string we might want to change the color.

We want the other string to start 180⁰ on from the start of the first. The code at the start of loop() calculates the time for a revolution and records the start time of the last revolution, so we just wait until half a revolution’s time from the start, after first checking we haven’t already gone past it!

if((micros() < (lastTime + revTime / 2))){

// wait for it . . .

   while((micros() < (lastTime + revTime / 2)) && !flag)continue;

    

//Now display the lower string

}

You can display the lower string (replace the comment) with a loop just like before, except that you’ll be displaying the characters in reverse order. Can you work out how to do that?

Don’t forget to switch off the color for the lower message if it was different from the upper one.

Lesson 5: How fast is it spinning?

In previous lessons the code calculated the time for a revolution, so as a final exercise, let’s display!

Actually, rotational speed is more often measured in revolutions per minute (RPM), but we can easily calculate that.

We calculated the time for a revolution in microseconds (revTime) and there are 60,000,000 microseconds in a minute, so the speed in RPM will be 60,000,000 / revTime. But we need to convert that to text.

The easiest way to do that is using the built-in sprint() function. This takes 3 (or more) arguments:

  1. Somewhere to put the resulting text
  2. A pattern for the text
  3. A value or values to insert into the text.

For the place to put the text we can replace the definition of textString_Up by:

char textString_Up[10];

We’ve made it 10 characters long, which should be plenty. It’s important not to make it too small otherwise we might be overwriting who knows what!

We can now call sprintf() with something like:

sprint(textString_Up, "%4d RPM", 60000000 / revTime);

The %4d means substitute this for the parameter expressed as a 4-digit decimal number. (That’s just one of many types of substitution you can specify.)

Why not change textString_Down to display the total number of revolutions?

Warning: Don’t try too hard to see how fast you can get it to spin – it has hard edges and you could easily hurt your fingers!

Appendix: C Language Survival Guide

The Basics

  • Statements are grouped together by curly brackets { }, not by indentation (as in Python).
  • You can put newlines and spaces where you like, but your code will be easier to read if you indent it (like Python).
  • Everything between // and the end of the line, or between /* and */ is a comment.
  • All variables must be declared, defining their type. They have no meaning outside the smallest enclosing { }. Declarations must be the first things after {.
  • All statements (including declarations) must be terminated by a semicolon.
  • Lines beginning with # are definitions of constants or directives to include a file from somewhere else containing stuff you’ll be needing.

Declarations

int a, b, c; // Declare 3 integers

float x; // Declare a floating point variable (needn't be a whole number)

boolean love_marmite; // Takes the values true or false

char c; // this variable holds a single character

int blindmouse[3]; // declares blindmouse[0], blindmouse[1] and blindmouse[2]

Statements

See the Arduino Reference (under Help in the Arduino IDE) for a list of operators.

Assignment

x = (a + b) / c;

Conditional

if ( a == 0 ) then { ….}

if (x >= 0 && x < 10} then { …. } else { …. }

Loop

while ( sum < max ) do { …. }

for (i=0; i<10; i++) { ….} // The same as i = 0; while (i < 10) { ….; i = i+1; }

break; // Exit immediately from the smallest surrounding loop

continue; // Go back to the top of the smallest surrending loop and start the next time round.

Functions

  • A function has a type, but if it just does a job and doesn’t return anything the type is void.
  • We have to declare the type of the parameters (length and breadth), to which are assigned the values given in the function call.
  • See the Arduino Reference for standard functions you can use.
float area(float length, float breadth) {

return length * breadth;

}

roomsize = area(6, 9);
Advertisements