Engduino EXAMPLES: Theremin

advertisement
ENGDUINO EXAMPLES: THEREMIN
The theremin is a weird and wonderful electronic instrument that requires no physical contact. Have
a listen to a program about them at:
http://www.bbc.co.uk/programmes/b0076nqv
The theremin was invented in 1920 by Léon Theremin,
an early Russian electronic engineer. It is played by
moving one’s hands near two antennas – the first
controls the volume of the output and the second the
pitch.
For those that are musical it is worth knowing that the Theremin inspired Robert Moog to invent the
synthesiser, so, although it’s a little-used instrument, it has had a powerful effect on the history of
music.
THE PROJECT
In this project, we will use the light sensor to control the frequency of a tone. We could use a second
Engduino to control the volume, but that requires a bit more hardware and it’s the principles that
matter here.
You will need an Engduino and a little piezo-electric speaker. Plug the speaker into the Engduino’s
expansion bus – to the connections marked IO_0 (third from top) and GND (bottom).
The project itself falls into two parts: making a tone at a given frequency and controlling that
frequency using the light sensor.
PRODUCING A TONE
A tone is a pure sine wave. A sine wave of 440Hz is the A above middle C, commonly used for tuning.
Mixtures of different sine waves are what gives us the complex sounds we hear from musical
instruments. Pure tones on their own sound artificial and electronic because they don’t happen in
nature.
Unfortunately, our Engduino can’t even generate a
sine wave – it is a digital system so can only generate
square waves (0s and 1s). For our purposes, this
doesn’t matter much – in fact a square wave can be
represented by a series of mixed sine waves so it is
not quite the same as a pure tone, but it’s close
enough for us.
WRITING OUR OWN CODE TO MAKE A TONE.
If you’ve connected the speaker up as suggested, you have connected it to digital pin D5. To make a
very boring sound, we can switch the output from 0 to 1 and back again at a known frequency. For
example:
int speaker = 5;
void setup()
{
pinMode(speaker, OUTPUT);
}
void loop()
{
digitalWrite(speaker, HIGH);
delay(1);
digitalWrite(speaker, LOW);
delay(1);
}
// Speaker connected to digital pin 5
// sets the digital pin as output
//
//
//
//
sets the speaker pin high
waits for a 1ms
sets the speaker pin low
waits for a 1ms
In setup, we set the digital pin to be an output. Then we loop setting it high, waiting for 1ms, setting
it low, waiting for 1ms, and continuing. What frequency note would you expect this to be? See what
happens when you change the delay values.
AN ALTERNATIVE
Fortunately, someone has done the hard work and written a library for us that will generate tones.
The library uses a feature built in to the microprocessor called pulse width modulation (PWM) – in
our case this just allows us to generate tones more accurately than we could do ourselves.
There are two calls – one to make a tone, or make it for a given amount of time; and the second to
stop a tone being played:
tone(speaker, frequency)
tone(speaker, frequency, duration)
noTone(speaker)
Write a second sketch to try it – using the frequency you thought the previous tone was at to check
whether you’re right. See if you can make it play a tune. HINT: the frequencies of notes are given at
the bottom of the page at http://arduino.cc/en/Tutorial/tone.
GETTING INPUT FROM THE LIGHT SENSOR
You should work through the light sensor worksheet.
Print the light values to the screen. Even if keep everything as constant as possible – don’t move the
Engduino, sit still, etc., is the value a constant? If we use this value to change the sound, what kind of
thing do you think we’ll hear?
RESCALING THE INPUT
The input from the light sensor ranges, in theory, from 0-1023. In practice it will rarely reach 1023
and, if you’re just using the illumination in the room, the maximum in practice might only be, say,
700 (you’ll need to measure this). Likewise, the minimum might not be zero when it’s fully covered –
try it.
Choose a range of frequencies you’d like your theremin to cover. Let’s say that’s 50Hz to 5000Hz.
So, in my case, I would want the following:
0
700
50
5000
Input from light sensor
Output frequency
See if you can work out how a way of calculating the output frequency for a given value of light
input.
Try it – print the original and scaled values to the screen using Serial.print
CONNECTING THE TWO
We now need to connect the light sensor to the speaker.
Use the rescaled value you’ve just generated to set the tone on the speaker. How does it sound?
Awful, in all probability. And there are two problems. The first is that your hand has to get very very
close to the light sensor to get low tones and the second is that it just sounds awful. Let’s fix the
second.
PLEASE STOP, YOU’RE HURTING MY EARS
The problem with the tone that never seems to stand still comes from the fact that the light sensor
value doesn’t stand still – it is noisy, partly because the lights flicker, albeit faster than you can see.
So we need to filter the value – to smooth it out over time.
To do that, we might make use of some maths. We could read the sensor and make an average out
of the last n readings, where n is a number you choose. This is called a simple moving average and,
mathematically, it’s represented by the formula:
𝑜𝑢𝑡𝑖 =
𝑖𝑛𝑖 + 𝑖𝑛𝑖−1 + 𝑖𝑛𝑖−2 + 𝑖𝑛𝑖−3 + ⋯ + 𝑖𝑛𝑖−𝑛+1
𝑛
Try to implement this. To do so, you’ll need to know what an array is.
ASIDE: ARRAYS
An array is a data structure used in just about all programming languages. In our case, it’s a
numbered collection of things of the same type with a fixed maximum size. In essence it’s a set of
boxes into which we can put values – each box has a number, starting at 0 and going up.
Say we needed to keep the last 3 values of a variable, then we create an array of length 3. Let’s say
these values are floating point values rather than integers and we want the name of the array to be
‘buffer’. We declare the array immediately after the #include directives in the following way:
#define BUFFERSIZE 3
float buffer[BUFFERISZE];
This creates an array of 3 boxes (labelled 0, 1, and 2) in which we can store floating point numbers.
But, before we can use this for our purposes, we need to know where to put the latest sensor
reading. For this we need another variable that will tell us which of those spaces should contain the
value we have just read from the sensor (i.e. in box 0, 1 or 2) – we’ll call this the index variable. We
will calculate the average of all the readings stored in buffer and, if we keep moving the index value
on, we can make sure that we always have the last three values from the sensor in the array. After
some time, let’s imagine we have to deal with reading 27. Before we put it in, the buffer should
contain readings 24, 25 and 26:
0
1
2
25
26
24
index = 2
After we put in 27, we want it to contain:
0
1
2
25
26
27
index = 0
When we get reading 28, this gets put into box 0…
0
1
2
28
26
27
index = 1
And so on. We just need to organise that the index variable always points to the next box into which
we should put a reading. To achieve this, add the following after the above two lines – it creates a
variable ‘index’ and sets its value to be 0 (the first location in buffer).
int index = 0;
Now let’s look at how to use this in calculating a simple moving average. In the loop() section of your
code, you will need something that looks a bit like the following:
void loop()
{
…
int sensorValue;
int rescaledValue;
float movingAverage;
...Get the light sensor value
buffer[index] = sensorValue;
index = index + 1;
if (index > BUFFERSIZE)
index = 0;
movingAverage =
for (int i = 0;
movingAverage
movingAverage =
0;
i < BUFFERSIZE; i++)
= movingAverage + buffer[i];
movingAverage/BUFFERSIZE;
... Rescale the moving average value to the range we want
... and play the tone
delay(1);
}
Give this a go….
BACK AGAIN
The longer the buffer we choose, the smoother the value will be, but the longer the instrument
takes to respond to a change. Welcome to engineering.
Experiment with this. Why does the instrument sound odd for a short while after you reset it and
what could we do to change that?
RESCALING THE INPUT REVISITED
One of the reasons that a simple scaling doesn’t work so well – much of the change happens close to
the light sensor – is that the input is not linear in the distance your hand is from the sensor. Take a
tape measure and work out what the (averaged) light sensor readings are for each distance. Plot this
as a curve.
Can you figure out a way of changing your rescaling so that we can make changes (even a bit) further
away than at present?
ENGDUINO EXAMPLES: THE GIMBAL
In this example we’ll build a simple motorised gimbal. Gimbals are an ancient
device used to stabilise, for example, compasses on board ships. In short they are
designed so that when the ship moves, the compass stays flat.
Recently, with the introduction of small motors and small
cameras, motorised gimbals have been used in photography –
both for handheld cameras and for those mounted on, say,
helicopters or UAVs. In these, a sensor detects changes
movement of the person/helicopter and causes the motor to
move the camera to compensate. And so the picture stays steady.
We will use the accelerometer to detect where down is and a servo motor to move the Engduino so
that it stays in the same orientation. To do this, we’ll have to learn a little control theory – and you
will undoubtedly find out that it requires a bit of time and effort to make a good(ish) control system.
To build this project we need to do two things: (i) figure out which direction is down relative to how
the Engduino is being held; and (ii) move the servo motor to compensate. The second is itself two
parts: (iia) learn how to move the servo motor to a given place; (iib) choose how much to move it in
one go.
WHICH WAY IS DOWN?
You should complete the accelerometer handout before proceeding.
The accelerometer returns three values – x, y, and z. If we’re not accelerating the device, then the
values are due exclusively to gravity and we can figure out what direction gravity is in just from the
values returned.
First, we need to know what direction is what. Look at the x, y, z values as you move the Engduino in
space. Check that the directions on the picture match the direction you think gives a positive value
for each axis.
z (out of the board)
y
x
So, let’s make the problem a bit easier. If we hold the Engduino vertically (so the E always faces out
from your chest), then the z axis will never have any acceleration due to gravity, irrespective of how
the Engduino is rotated. So we only need to think about the x and y axes.
There is a useful function for working out angles from x and y values. The function is an arctangent –
the opposite of a tangent.
tan θ = y/x
θ = arctan(y/x)
y
θ
x
In Arduino code, the arctan function is represented by something called atan2(y,x) (and it is
important to remember the 2): it’s a slightly more sophisticated form of arctan that gives a value
between -180° and +180°
You need to add:
#include <math.h>
to the top part of the file with the other includes, and then you can use the atan2 function:
angle_in_radians = atan2(accelerations[1], accelerations[0]);
Amend the code you wrote for the accelerometer to print out this angle, then rotate the Engduino
(remembering to hold it so the E always faces out from your chest) so the angle changes.
The number seems to go from about -3.14 to +3.14 – what’s happening? Well, this is an angle in
radians, and that’s perhaps something you aren’t used to, but it is no big deal – it is just another way
of writing angles that happens to be useful to mathematicians. There are 2π radians in a circle – put
another way 2π radians is the same as 360° or π radians is the same as 180°. So when we see
numbers that go from -3.14 (i.e. –π) to 3.14 (i.e. + π), what we’re actually seeing is the number going
from -180° to +180°. If you want to convert between an angle in radians and one in degrees we write
the following code:
angle_in_degrees = angle_in_radians * 180/PI;
Note the capital letters for PI. Try it.
So, with the E the right way up and out from your chest, what angle would you expect? (Answer -90,
but why?)
MOTORS
Next we’re going to take a look at the servo motor. These are used in radio
controlled vehicles and aeroplanes to control, for example, the angle of the rudder
of a boat, or the ailerons on a plane. They are a motor we can set to a certain
angle – in this case ranging from 0° to 180°– only half a circle.
The motors are set using what is called a pulse width modulation signal (PWM).
1ms
0
90
1.5ms
2ms
180
Time /ms
0
20
WRITING OUR OWN CODE TO DRIVE THE MOTORS
We can write a program to generate these by hand. Get the assistant to connect the device up for
you. The servo motor control signal is connected to digital pin 5 on the Engduino.
int motor = 5;
void setup()
{
pinMode(motor, OUTPUT);
}
void loop()
{
digitalWrite(motor, HIGH);
delayMicroseconds(1000);
digitalWrite(motor, LOW);
delayMicroseconds(19000);
}
// Motor connected to digital pin 5
// sets the digital pin as output
//
//
//
//
sets the LED on
waits for 1ms
sets the LED off
waits for 19ms
This generates the first square wave (on for 1000μs = 1ms off for 19000μs = 19ms). It won’t quite
work to set it to location zero because there are some extra delays in the instructions that make the
on-time a little more than 1ms.
Experiment – see what works.
AN ALTERNATIVE
Fortunately, someone has done the hard work and written a library for us that will move the servo
motor. The library the pulse width modulation (PWM) that is built into the microprocessor.
To use the library we have to ‘attach’ ourselves to the servo in the setup() routine and then we can
command it to go to a particular angle by ‘write’ing a position in degrees. The code below is an
example from the authors of the library – it can be found at http://arduino.cc/en/Tutorial/Sweep to
save you retying it (but you must remember to change the pin to 5). See what it does.
// Sweep
// by BARRAGAN <http://barraganstudio.com>
// This example code is in the public domain.
#include <Servo.h>
Servo myservo;
// create servo object to control a servo
// a maximum of eight servo objects can be created
int pos = 0;
// variable to store the servo position
void setup()
{
myservo.attach(5);
}
// attaches the servo on pin 5 to the servo object
void loop()
{
for (pos = 0; pos < 180; pos += 1) // goes from 0 degrees to 180 degrees
{
// in steps of 1 degree
myservo.write(pos);
// tell servo to go to position in pos
delay(15);
// waits 15ms for the servo to move
}
for (pos = 180; pos>=1; pos-=1)
{
myservo.write(pos);
delay(15);
}
// goes from 180 degrees to 0 degrees
// tell servo to go to position in pos
// waits 15ms for the servo to move
}
So now we can set the servo to a known angle. In reality, we don’t necessarily quite get the full 0180 degree range, but we can let that go.
MAKING A GIMBAL
Ask the class leader to attach the servo to the Engduino if it is not already attached. This just needs
to be done so that the Engduino can move in the right way (set the servo to angle zero and then
attach the servo with the top facing the same direction as the E).
Our goal is to create a system that ensures that the Engduino is always facing the right way up, no
matter how we move it (with the bounds of what the motor can do). We know ‘the right way up’ is
to have an angle of -90°. In control terms, this is called the ‘setpoint’.
If the angle changes because we move the Engduino, then we need to make the servo move in such
a way as to compensate for that change. We do that by calculating an error between the setpoint
and the current angle.
double error = setpoint - angle;
ASIDE: CONTROL
If we have an error, then know exactly how far we need to move the servo to get to the correct
position, right? All we would have to do is to write some code in the setup routine that says:
myservo.attach(5); // attaches the servo on pin 5 to the servo object
myservo.write(pos);
And in the loop that says:
pos += error;
pos = constrain(pos, 0, 180);
myservo.write(pos);
delay(50);
The call to ‘constrain’ just ensures that the value always lies between 0 and 180 – the only sensible
range of inputs to our servo.
Try it – see what happens, but be prepared to hang on. Unless you’re supremely lucky the system
will be unstable. Let’s try something different - so called proportional control. What we do is to
multiply the error by a number less than 1 (i.e. a proportion) – say 0.1. Replace the first line above
by:
double Kp = 0.1;
pos += error * Kp;
Is it more stable? How about for different values of Kp, say 0.3? Keeping it in the same orientation,
just shake it slightly and see what happens. In general, the higher the value of Kp, the more quickly it
will get to the setpoint, but the less stable it will be.
What about more sophisticated control?
Well, a common form of controller is a PID controller – proportional, integral, differential.

Proportional control is what we’ve just done – it depends on a proportion of the current
error.


Integral control adds up error over time. The bigger the accumulated error, the bigger the
control input (e.g. the more off course a ship has become in spite of proportional control,
the more sharply we should steer)
Differential control depends on the rate of change. If we get a sudden change in something
then we guess that that change will persist into the future – it’s a guess about what might
happen.
Pseudocode would look something like the following:
error = setpoint - sensor_value;
errorSum
+= (error * timeChange);
errorChange = (error - lastErr) / timeChange;
p = Kp * error;
i = Ki * errorSum;
d = Kd * errorChange;
pos = pos + p + i + d;
lastErr = error;
You could code this up and try some parameters. What you are likely to find is that many
parameters do not lead to sensible behaviour, and it takes a little while to get a set that is much
good – reasonably stable and with a reasonably rapid response. Fortunately, in real life there is
maths to help with all of this – and some tricks on implementation that make the results better…
welcome to Engineering.
Control systems are used in all factories, cars, homes, in the production of electricity and the
pumping of water, in rockets and in robots for surgery. Building them is a many many billion dollar
business and there’s quite a lot to learn.
Take a look at the Arduino PID library http://playground.arduino.cc/Code/PIDLibrary - and, in
particular, the detailed description of how to improve our beginners’ version of PID
http://brettbeauregard.com/blog/2011/04/improving-the-beginners-pid-introduction/
ENGDUINO EXAMPLES: COMMUNICATIONS
Humans have been using simple communications systems for centuries to send messages over a
long distance, much further than the human voice can reach. Information was sent between
signal towers on the Great Wall of China by using flashing lanterns at night or smoke signals
during the day. Around 100 BC a Roman historian called Polybius invented a way of using
numbers to represent the alphabet using a grid.
1
2
3
4
5
1
A
F
L
Q
V
2
B
G
M
R
W
3
C
H
N
S
X
4
D
I/J
O
T
Y
5
E
L
P
U
Z
Table 1- Polybius' Grid
Words can be spelled out using the grid reference of the letters so ‘A’ is 1,1 and ‘S’ is 4,3. The
message “RED ROSE” is: (4,2) (1,5) (1,4) (4,2) (3,4) (4,3) (1,5). Polybius invented this scheme so
that people could send a message using 2 torches. Imagine the sender holding a torch in each
hand. Flashing the torch in the right hand to indicates the row of the letter in the grid. Flashing
the torch in the left hand to indicates the column of the letter.
With another person:
One of you should encode a short, secret message using the Polybius Grid. Find a way of
communicating your message to the other person without speaking. Let the other person
decode the message.
IN THIS PROJECT
You will learn how to send and receive secret messages using two Engduinos. You will transmit
messages using the LEDs on one device and receive messages using the light sensor on the other.
The point of this project is to:

learn about the way information is stored in a computer using binary,

explore how information is transmitted between one computer and another.
GETTING STARTED
Computers only understand a very simple and limited language composed of ‘0’s and ‘1’s. This is a
binary number system which you have probably learned about already. It is very easy to generate
signals to represent this two-digit language, after all, we can think of the digit 0 as OFF and the digit
1 as ON. The 0 and 1 are known as ‘bits’.
Programming the Engduino to send and receive binary messages is quite complex but we can make
it easier by breaking the project down into stages:
Stage 1.
Make one Engduino talk by sending a binary signal.
Stage 2.
Make the other Engduino listen by receiving a binary signal.
Stage 3.
Synchronise the sender and receiver.
Stage 4.
At the sender, convert a character in a message to a binary signal.
Stage 5.
At the receiver, convert binary signal to a character in a message.
You are going to complete this project by writing your own code and by using some code
we have written for you.
Download the file containing the sketches you need from the Engduino website. The file is
called SendReceiveSketches.zip. Save it on the computer’s Desktop or
somewhere inside your Documents folder.
To ‘unzip’ the file, right click on it and choose option “Extract All …..”
STAGE 1: SEND A BINARY SIGNAL WITH THE ENGDUINO
Complete the Traffic Lights tutorial before attempting this part of the project.
We have seen that you can communicate quite complex messages using a simple coding system like
the Probius Grid. Our first task is to program one of the Engduinos to send a binary signal: ‘0’ (OFF)
and a ‘1’ (ON) using the LEDs. We can use some of the code from the Traffic Lights tutorial to do this.
SEND A SIGNAL
Start the Arduino IDE. Open the sketch SendBinary.ino. Use File -> Open like this:
You can find this file in the SendBinary sketch in the folder you downloaded.
Now write some code to turn all the LEDs on for, say 1 second and then turn the LEDs off for 1
second repeatedly. You should add a definition for the length of time it takes to transmit a bit. Add
this immediately after the #include directives in the following way
#define BIT_DURATION 1000 //Length of time (milliseconds) to transmit a bit
Then later in the main loop(), you can use this value like this:
delay(BIT_DURATION);
Don’t forget to save your program intermittently. Upload the program to the Engduino that will be
the sender in your communications system.
Your Engduino is now transmitting a series of bits like this:
01010101010101010101010101010101
STAGE 2: RECEIVE A BINARY SIGNAL WITH THE LIGHT SENSOR
You should work through the Light Sensor worksheet.
The next step is to write some code for the Engduino receiver that will interpret the binary signal.
The question we need to answer is: are the light sensor readings significantly different when the
LEDs on the sending device are on and off?
RECEIVE A SIGNAL
Open the file ReceiveBinary.ino which is in the ReceiveBinary folder.
Write some code to read the values from the light sensor and print out a message to the serial
monitor repeatedly. You will need to upload your program to the second Engduino, the one you are
using as a receiver. Make sure it is plugged into a USB port and that you have the correct COM port
selected in the Arduino IDE.
The input from the light sensor ranges, in theory, from 0-1023. In practice it will rarely reach 1023
and, if you’re just using the illumination in the room, the maximum might only be, say, 700 (you’ll
need to measure this). Likewise, the minimum might not be zero when it’s fully covered – try it.
Shine the Engduino with the flashing LEDs directly at the light sensor on the receiver.
What is the light level when the LEDs are off?
What is the light level when the LEDs are on?
Add some more code to the receiver. If the light level is high then print a message to the serial
monitor to show that you have received a ‘1’. Otherwise, if the light level is low then print a message
to the serial monitor to show that you have received a ‘0’.
Congratulations! You have just created a communications system with a sender and a receiver.
STAGE 3: SYNCHRONISE THE SENDER AND RECEIVER
You should work through the Button worksheet.
Imagine that you have sent this binary message.
1000110111111000011010110011
As you can see, there are some repeated digits. In some places there are two, three or even more
repeated 0’s or 1’s. How will the receiver know how many 0’s or 1’s it has received?
In a computer system, all binary signals are synchronised using an internal clock. The hardware
components in a computer use the clock to tell when to send a bit and when to receive a bit.
Our problem is more difficult to solve, we have two devices, a sender and receiver, and they don’t
share a clock. We need to synchronise the sender and receiver so that:
1. The sender and receiver know when to start transmitting and receiving.
2. The sender and receiver know how long an LED will flash for each binary digit.
We are going to have to synchronise the sender and receiver using some kind of manual technique.
One way that we can do this is to tell the Engduinos when to start by pressing the button on the
back of each device. This may not be very accurate but let’s see if it’s accurate enough.
ADDING A START SIGNAL
Add some code to the sender and the receiver so that they don’t start sending or receiving until the
button is pressed.
Now add a delay to the receiver so that it checks the light level every 1 second. Remember, the
sender is sending a bit every second.
You are now at the point where you can send and receive a signal fairly reliably. Let’s do one more
thing before we finish this stage. At the moment, our sender and receiver are synchronised as
closely as they can be. Both start sending and receiving as soon as we press the button and both
wait 1 second before sending and receiving again.
The diagram in Figure 1 shows what happens when the sender and receiver are synchronised
accurately enough.
The
Figure 1 - Sending and Receiving a Binary Digit
You can see that if there is a small delay at the receiver, things might be different. The receiver
might not read the correct bit. To reduce the possibility of this happening you can add a small delay
of 500 milliseconds to the receiver. Do this now, add the delay right after the code for waiting for
the button press. This will ensure that the receiver reads the light sensor roughly in the middle of the
transmission.
Next, we have to make that signal mean something – our message.
STAGE 4: TRANSLATE A LETTER OF THE ALPHABET INTO A BINARY SIGNAL
Transferring bits from one Engduino to the other is all very well but humans do not communicate in
0’s and 1’s; when we write things down we use an alphabet. Our next task is to write some code to
translate letters of the alphabet into binary at the sender so that they are ready for transmission.
The coding system which is most often used to represent the alphabet is known as ASCII which
stands for the American Standard Code for Information Interchange. At the time it was invented,
ASCII was used to encode 128 characters: 0-9, a-z and A-Z and some punctuation symbols.
Nowadays, ASCII has been superseded by UTF which stands for Universal Character Set,
Transformation Format. UTF can accommodate many different alphabets and symbols such as
Arabic and Kana, which is a Japanese script.
In ASCII, each character is represented as 7 or even 8 bits of binary. In this program we will use 7
bits. Table 1 contains the binary representations of the characters ‘A’ through to ‘E’.
English
Alphabet
A
B
Binary
C
1000011
D
1000100
E
1000101
1000001
1000010
Table 2 - ASCII to Binary Conversion
It would take a while to type in the ASCII conversion table into our program so we will use some
code that exists already. As programmers, we use and re-use code regularly, after all there is no
point in writing code yourself if someone else has already done it.
You have been given two extra sketches with your Sender and Receiver sketches called
ASCII2Binary.ino and Binary2ASCII.ino. Take a look at the files. Even if you don’t
understand the code yet, you will see something that looks like a lookup table near the top of each
one.
These files contain some simple conversion utilities. As their names suggests, one will convert an
ASCII character into seven binary digits. The other will convert seven binary digits into an ASCII
character.
Before we can use these utilities, we will have to learn how to store our binary digits in an array.
Let’s learn what an array is.
ASIDE: ARRAYS
An array is a data structure used in just about all programming languages. In our case, it’s a
numbered collection of things of the same type with a fixed maximum size. In essence it’s a set of
boxes into which we can put values – each box has a number, starting at 0 and going up.
Say we needed to store 7 binary digits, then we create an array of length 7. Let’s say we store these
values in an array of integers and we want the name of the array to be ‘binaryValue’. We declare the
array immediately after the #include directives in the following way:
#define ARRAYSIZE 7
integer binaryValue[ARRAYSIZE];
This creates an array of 7 boxes (labelled 0, 1, 2, 3, 4, 5, 6) in which we can store integer numbers.
0
1
2
3
4
5
6
1
1
0
0
0
1
0
The letter ‘b’ in binary
At the sender, we are going to translate an ASCII character to a set of binary digits then we are going
to make the Engduino flash on and off for each digit.
To do this, we need to go through each cell in the array in turn. For this we need another variable
that will tell us which of those spaces, or boxes, contains the digit we’ll transmit next – we’ll call this
the index variable.
We just need to organise that the index variable always points to the next box that we want to
transmit. To achieve this, add the following after the above two lines – it creates a variable ‘index’
and sets its value to be 0 (the first location in buffer).
int index = 0;
Now let’s look at how to use this.
BACK AGAIN
In the loop() section of your sketch, you will need something that looks a bit like the code below:
void loop() {
//
// Wait until the button has been pressed.
//
EngduinoButton.waitUntilPressed();
char nextCharacter = 'a';
// a character to transmit
//
// We have the next letter in our message, now lookup the binary value
// using the method ASCII2Binary.
//
ASCII2Binary(nextCharacter, binaryValue);
for (int index = 0; index < ARRAYSIZE; index++) {
if (binaryValue[index] == 1){
EngduinoLEDs.setAll(WHITE);
}
delay(BIT_DURATION);
EngduinoLEDs.setAll(OFF);
}
Give this a go…. add some print statements to your code using the Serial.print() function so
that you can see what it is doing using the Serial monitor.
SENDING A MESSAGE CONTAINING SEVERAL CHARACTERS
Now that you know how to use arrays you can create one in which to store your simple message:
#define MESSAGESIZE 5
char message[MESSAGESIZE]={'H', 'E', 'L', 'L', 'O'};
This time, our array contains characters. Add an array containing a message to your program. Use a
for loop to go through each character in the message and translate it to a binary value.
STAGE 5: TRANSLATE A BINARY SIGNAL TO A LETTER OF THE ALPHABET
The final stage of this project is to collect 7 bits of binary at the receiver and translate them into an
ASCII character.
Go back to editing the code for the receiver. In the same way as you did before, declare an array of
integers, length 7. Let’s say we we want the name of the array to be ‘binaryValue’, again, we declare
the array immediately after the #include directives in the following way:
#define ARRAYSIZE 7
integer binaryValue[ARRAYSIZE];
Now edit your code so that you are using the array to store the binary signals you are collecting.
Your code should look something like this:
void loop() {
int lightValue;
// Wait until the button has been pressed.
EngduinoButton.waitUntilPressed();
delay(BIT_DURATION / 2)
// Wait for a short time
//
// Detect 7 bits
//
for (int i = 0; i < ARRAYSIZE; i++) {
lightValue = EngduinoLight.lightLevel();
if ( lightValue >= LIGHT_ON )
binaryValue[i] = 1;
// Assume 1 received
else
binaryValue[i] = 0;
delay(BIT_DURATION);
}
// Assume 0 received
CONVERTING THE BINARY DIGITS TO A CHARACTER
Look at the sketch called Binary2ASCII, how do you think you should use it? Give it a go …..
WHAT NEXT?
In this tutorial you have covered the basic elements of all digital communications systems but there
are several ways in which our communications system could be improved. You have probably
noticed that sometimes the receiver does not receive the bit correctly. You could think about using a
parity bit for this, take a look at the Section on Error Detection and Correction in the A-Level
Computing Wikibook by Peter Kemp.
Download