CHAPTER 2 ■ APPLIANCE HACKING 68 In addition to light, you can also measure temperature (thermistors), distances (ultrasonic range finders), and rotary positions (by attaching a handle or wheel to the spindle of a potentiometer). Sending Digital Outputs This is as simple as it sounds and includes those cases where you want to send nothing more than a simple on/off to something like a light switch or small motor. The code requires a simple setup and invocation like this: int outputLightPin = 2; pinMode(outputLightPin, OUTPUT); // digitalWrite(outputLightPin, bState ? TRUE : FALSE); From here, it’s all about the circuit. Each pin on the Arduino is able to source current, up to around 40 milliamps. This is enough for most small loads such as lights, buffers, and some small motors. If your device uses no more than 300 milliamps, then you can draw this from the circuit board itself using the Arduino as a switch only with a circuit like that in Figure 2-5. Figure 2-5. A transistor switch circuit, allowing you to draw up to 300mA For anything larger, you will need to employ a relay and separate power source, as shown in Figure 2-6. CHAPTER 2 ■ APPLIANCE HACKING 69 Figure 2-6. Using a relay to control high power loads ■ Note In reality, the Arduino is good for higher power loads up to 800mA. But for dirty loads, such as motors, the 300mA is probably more conservatively sensible. Sending Analog Outputs As mentioned previously, analog output is available on the basic Arduino only when using PWM. This is supported only on pins 3, 5, 6, 9, 10, 11, and 21. 10 The pulsing of the output pin to provide the output is handled automatically, including the setup, so you need only to write this: analogWrite(analogWritePin, value); // value is between 0 and 255, inclusive This will allow you to vary the brightness of LEDs and the volume of piezo-buzzers and speakers, but not a lot else. In reality, you won’t need a lot else in home automation environs. Moving a motor to a specific position, for example, is better done with servo or stepper motors, and many other forms of positioning should be done through the use of feedback loops—switching the motor on to begin movement and switching it off when a sensor reveals it has gone far enough. 10 Older boards using the ATmega8 support only pins 9,10, and 11, while the newer Arduino Mega can support PWM on 12 of its pins. CHAPTER 2 ■ APPLIANCE HACKING 70 Creating Audio Outputs This is one of the simplest forms of human-friendly feedback the Arduino can provide, because the circuit needs only a buzzer attached to a digital output pin. The code is a simple loop changing the output state between on and off 440 times a second, for example, to produce the A note above middle C. The examples on the main Arduino site provide the necessary code to make it beep and play short tunes. For those looking for more advanced music control, there is Armstrong (www.bluedust.dontexist.com/armstrong), the Arduino music system, which provides a small melody processor allowing you to play the chimes of Big Ben with code like this: ancInitialize(OUTPUT_LOCAL); ancAssignChannelToPin(CHANNEL_OUTPUT_PIEZO_SPEAKER, piezoPin); char *pChimesPhrase1 = "L32O4edcL64O3g"; char *pChimesPhrase2 = "L32O4cedL64O3g"; char *pChimesPhrase3 = "L32O4cdeL64c"; char *pChimesPhrase4 = "L32O4ecdL64O3g"; char *pChimesPhrase5 = "L32O3gO4deL64c"; // a quarter past ampPlayString(CHANNEL_OUTPUT_PIEZO_SPEAKER, pChimesPhrase1); // half past ampPlayString(CHANNEL_OUTPUT_PIEZO_SPEAKER, pChimesPhrase2); ampPlayString(CHANNEL_OUTPUT_PIEZO_SPEAKER, pChimesPhrase3); // a quarter to ampPlayString(CHANNEL_OUTPUT_PIEZO_SPEAKER, pChimesPhrase4); ampPlayString(CHANNEL_OUTPUT_PIEZO_SPEAKER, pChimesPhrase5); ampPlayString(CHANNEL_OUTPUT_PIEZO_SPEAKER, pChimesPhrase1); // top of the hour ampPlayString(CHANNEL_OUTPUT_PIEZO_SPEAKER, pChimesPhrase2); ampPlayString(CHANNEL_OUTPUT_PIEZO_SPEAKER, pChimesPhrase3); ampPlayString(CHANNEL_OUTPUT_PIEZO_SPEAKER, pChimesPhrase4); ampPlayString(CHANNEL_OUTPUT_PIEZO_SPEAKER, pChimesPhrase5); Using the various shields provides more complex audio output, including sample playback. Communication with a PC The basic Arduino allows bidirectional communication with a PC through its built-in USB port. This uses the same serial port that’s used to upload the program in the first place. It must be set up first before data can be sent or received, both of which are supported with this method: Serial.begin(9600); The Arduino can read data from the PC only on a byte-by-byte basis, so you will need to read it within a loop using code like this: CHAPTER 2 ■ APPLIANCE HACKING 71 int incomingData = Serial.read(); Note, however, that this function is blocking. That is, it will not return from the read function until there is data on the serial port. If your device responds only to messages, then this will work fine. However, it is more usual to place this at the start of your loop, surrounded with this: if (Serial.available() > 0) { /* */ } Writing data from the Arduino back to the PC, however, is easier since the size of the data is already known. This can be handled by the Serial.print or Serial.println function for quick and easy string messages. Or individual bytes can be written using Serial.write: Serial.write(byteData); This is useful for applications wanting to transfer a lot of data. But for testing and development, I find a simple ASCII-based protocol easier to work with, because a simple serial terminal allows me to send and receive messages in human-readable text. The Minerva feature, MINX (Minerva INput transfer), uses the delimiters <| and |> to surround control messages, allowing an Arduino to pass messages back to the PC so that further processing can take place, without affecting any other debugging or trace messages. I’ll cover this fully in Chapter 7. ■ Note Some models of Arduino, such as the Mega, have three additional serial ports addressable as Serial1, Serial2, and Serial3. On the PC side of this transmit-receive equation, you have a much simpler job since everything in Linux is treated like a file. You can therefore issue this: tail -f /dev/ttyUSB0 To see all the data that is sent back from the Arduino and introduce commands to the board, use this: echo -n send data > /dev/ttyUSB0 It is from this that demonstrates the benefit of an ASCII-based protocol. In the simplest case, you can issue this: Serial.print("1"); from the Arduino to switch the PC control program into an on state, with an equivalent for off. This makes the C program very simple: CHAPTER 2 ■ APPLIANCE HACKING 72 // USB script trigger #include <stdio.h> char *szUSBDevice = "/dev/ttyUSB0"; int main() { char v; FILE *fp = fopen(szUSBDevice, "r"); if (!fp) { printf("Failed to open USB "); return 1; } while(1) { if (fread(&v, 1, 1, fp)) { if (v == '1') { // button pressed } else if (v == '0') { // button released } else { printf("%c", v); fflush(stdout); } } } return 0; } This also includes the functionality to write noncontrol codes to stdout, which may be redirected to a log file. If you compile the previous program with g++ as arduino_read, you can start it as a process in the background, making it daemon-like for very little effort: ./arduino_read > logfile 2>&1 & Arduino Hardware For some complex applications, the ATmega168 chip on the Arduino board is not powerful enough to handle the task. So, in common with other computers, additional chips are necessary to ease the burden. In keeping with the modular design of the Arduino, these chips are built up into stand-alone boards with the necessary interfacing circuitry so that they can be directly mounted on top of the existing Arduino board. For this reason, they are known as shields. Most shields also include a pass- through so that other shields can be placed on top of it. There is then a separate software library that is used to control the device and that is copied into the Arduino/hardware/libraries directory. ■ Note Not all shields are compatible with all models of Arduino. . of piezo-buzzers and speakers, but not a lot else. In reality, you won’t need a lot else in home automation environs. Moving a motor to a specific position, for example, is better done with servo. both of which are supported with this method: Serial.begin(9600); The Arduino can read data from the PC only on a byte-by-byte basis, so you will need to read it within a loop using code. in common with other computers, additional chips are necessary to ease the burden. In keeping with the modular design of the Arduino, these chips are built up into stand-alone boards with the