Generating Video Signals with an Arduino - Eleven Arduino Projects - Arduino: A Quick-Start Guide, Second Edition (2015)

Arduino: A Quick-Start Guide, Second Edition (2015)

Part II. Eleven Arduino Projects

Chapter 8. Generating Video Signals with an Arduino

So far we’ve used several different technologies to communicate with the outside world. We’ve used LEDs to represent our binary die’s results, for example, and we’ve used the serial port to send more elaborate messages. We’ve also turned data received on the serial port into shiny applications running in our browser.

For many projects this way of displaying information is sufficient, but in some cases you want a real display. You could use an LCD display, for example, and you’ll find multicolor TFT touch displays you can attach to the Arduino, too. Another option is surprisingly cheap: you can connect the Arduino to your TV set and display information right on the screen.

In this chapter, not only will you learn how analog TV works in principle, you’ll also learn how to generate a stable monochrome video signal using your Arduino. At the end of the chapter, you’ll have a graphical thermometer that will run on the TV set in your living room.

What You Need

images/parts_video

1. An RCA cable

2. A 470Ω resistor

3. A 1kΩ resistor

4. Some wires

5. A TMP36 temperature sensor from Analog Devices

6. An Arduino board, such as the Uno, Duemilanove, or Diecimila

7. A USB cable to connect the Arduino to your computer

How Analog Video Works

Before you create your own video signals, it helps to understand how analog TV systems work in general. If you’re impatient, you can skip the theory and jump straight to Connecting the Arduino to Your TV Set.

First of all, you should note that analog video is completely different from digital video in most regards. In this chapter, we’ll only talk about analog video signals that you can feed to your TV set’s composite input.

You might remember the good old days when TV sets were huge, heavy boxes with ridiculously tiny screens. These boxes had to be so big because they contained a small electron cannon that produced images by firing electrons to the screen. The cannon drew an image line by line—that is, it started at the top-left corner of the screen and drew the first line of the image. At the end of the line, it moved back to the left side and drew the second line. This technique is called raster scan. Figure 23, How raster scan works shows how it worked.

images/raster_scan


Figure 23. How raster scan works (Image created by Ian Harvey)

After the last line was drawn, the electron beam moved back to the top and drew the next image. Depending on the TV standard, this process happened 50 to 60 times per second, and it was fast enough to create the illusion of motion. (Actually, most TV sets needed two passes to draw a single image using a mechanism called interlacing, but for our purposes that’s irrelevant.)

Moving the electron beam across the screen isn’t sufficient. You have to somehow encode the information you’d like to draw. Therefore, you have to change the electron beam’s intensity while it traverses the screen. Due to a chemical reaction, the TV screen will glow in different colors when the electron cannon hits it with different intensities. For a monochrome signal, you need to generate the voltages explained in the following table.

Voltage:

0.0V

0.3V

0.6V

1.0V

Color:

SYNC

Black

Gray

White

A voltage of 0V represents the SYNC signal. It tells the TV set that a new line of the image begins. All the other voltages represent different colors. To draw a white dot, you have to set the voltage to 1V.

All we have to do is create a couple of different voltage levels. That doesn’t sound too difficult, but unfortunately, the Arduino has only analog input pins. It cannot emit analog output signals. At least not directly, but in the next section you’ll learn how to do it.

Building a Digital-to-Analog Converter (DAC)

The Arduino doesn’t natively support analog output signals, so we need to find a way around this limitation. The solution is a digital-to-analog converter (DAC).[77] As the name suggests, such a circuit turns a digital input into an analog output. You can find DACs in a lot of consumer devices—for example, in your MP3 player. It turns your digitally encoded music into an analog sound wave.

One of the most important characteristics of a DAC is its resolution. In our case we need to generate four different voltage levels to create a video signal. To encode four voltage levels, we need two bits—that is, our DAC has a 2-bit resolution. The following table shows how we could map the four binary input values to their voltage levels.

Binary input:

00

01

10

11

Output voltage:

0.0V

0.3V

0.6V

1.0V

We can use two of the Arduino’s digital pins to control the DAC’s input value, but we still have to find a way to generate different voltages depending on the pins’ values.

There are several ways to achieve this, but one of the easiest is by using a binary-weighted DAC. It has the following characteristics:

· You need a resistor for every input bit.

· All resistors have to be in parallel.

· Resistor value for bit #0 is R. For bit #1 it’s 2R, for bit #3 it’s 4R, and so on.

Let’s say we use the Arduino’s digital pins D7 and D9 to control the DAC’s input value. In the following figure, you can see our DAC’s circuit. You have to add the 470Ω and 1kΩ resistors yourself, but you get the 75Ω resistor for free, because it’s part of your TV set’s input connector.

images/voltage_division

In principle, the binary-weighted DAC is a voltage divider[78]—that is, it turns an input voltage into a smaller voltage. The output voltage depends on the resistor values—75Ω, 470Ω, and 1kΩ, in our case. If you set both input signals to 0V, the output voltage will be 0V, too. That’s how we can create the SYNC signal.

Calculating the output voltage for the remaining three combinations of input values isn’t rocket science, but the theory and formulas of voltage division are beyond the scope of this book. Just to give you a feeling, the following figure shows how to calculate the output voltage when you set D7 to 0V and D9 to 5V.

images/voltage_division_calc

The following table shows the corresponding output voltages for all possible combinations of pin values.

Pin D7

Pin D9

Output Voltage

Color

0V

0V

0.0V

SYNC

0V

5V

0.3V

Black

5V

0V

0.65V

Gray

5V

5V

0.95V

White

Now you should see why we’ve used a 470Ω and a 1kΩ resistor. The value 1000 is roughly 470 times 2, so the resistor values follow the rules of a binary-weighted DAC. Also, these two resistors (combined with the TV set’s 75Ω resistor) produce the output voltages we need. Note that the output voltages don’t exactly match the specification, but in practice the small differences are negligible.

Connecting the Arduino to Your TV Set

Even with a digital-to-analog converter in place, we still have a problem: the Arduino doesn’t have an RCA jack—that is, you cannot plug an RCA cable into an Arduino. We could attach an RCA jack to a breadboard and connect it to the Arduino, but there’s an easier solution. We’ll modify an RCA cable and connect it directly to the Arduino.

First, you have to cut off one of the cable’s connectors using a wire cutter. (See Learning How to Use a Wire Cutter, to learn more about wire cutters.) Then remove about three centimeters of the cable’s outer insulation. Be careful, because the insulation isn’t very thick. Use the wire cutter to cut it slightly and then remove it by pushing it slowly toward the cable’s end. The cable should look like the following image.

images/rca_cable_without_outer_insulation

As you can see, there’s a mesh of wires below the outer insulation. Bring back the mesh into wire shape by rubbing it between your thumb and forefinger, so you can solder it to a solid-core wire later. The result should look like the following image.

images/rca_cable_inner_insulation

The cable usually also contains an inner insulation made of plastic. Use the wire cutter again to remove the inner insulation. In my experience, it’s best to put the insulation between the wire cutter’s blades and then turn the cable slowly and carefully, increasing the pressure while turning the cable. Be very careful that you don’t accidentally cut the signal wire! After you’ve cut through the whole insulation, you can easily remove it. You should now see the cable’s signal wire, and your cable should look like the following image.

images/rca_cable_unwrapped

Finally, we have to connect the two resistors to the RCA cable’s signal wire, and it’s not sufficient to simply knot them together. You have to solder them. While you’re at it, connect the RCA cable’s ground wire to a piece of solid-core wire, so you can easily attach it to the Arduino. The following image shows what it should look like.

images/modified_rca_cable

That’s it! You’ve turned an RCA cable into a binary-weighted DAC that you can can connect to your Arduino to generate your own video signal. Plug the 470Ω resistor into port D7, the 1kΩ resistor into D9, and the ground wire into one of the Arduino’s GND ports. You can see the final circuit in the following image.

images/video_circuit

Using the TVout Library

Okay, the hardware’s done, but how do we bring it to life? We could try to write our own library to emit video signals, but I have to admit that I didn’t tell you the whole truth. To generate a clean and stable video signal, you not only have to output different voltages, but you also have to make sure that you emit your signals according to a very accurate schedule. The timing has to be so accurate that you have to implement it in assembly language!

Don’t worry! Of course there’s a library for that. The TVout library[79] not only generates crystal-clear video signals, but also comes with a lot of utility functions for drawing geometric shapes. On top of that, it supports different fonts in several sizes.

Note that the TVout library doesn’t support every Arduino board. For example, it won’t work on the Arduino Leonardo or the Arduino Due. Check the TVout’s website for a list of compatible hardware.

Download TVout,[80] unzip it, and copy the contents of the zip archive to the libraries folder of the Arduino IDE. Then restart your IDE.

The library comes with a few examples. The most important ones are DemoNTSC and DemoPAL. In principle, it’s only one example that demonstrates all of the library’s features, but it comes in two flavors: NTSC and PAL. This is necessary because there are different standards for analog TV. NTSC and PAL are two very popular ones. They don’t differ much, and modern TV sets are usually capable of working with both. Still, your TV set might be pickier about its input. If you’re living in the United States, you’ll probably need the NTSC demo; in Europe, PAL is the way to go.

images/rotating_cube

Compile and upload the sketch to your Arduino; then connect the Arduino to your TV set’s composite input using the RCA cable. You should see an impressive demo showing TVout’s capabilities. At the end it even shows the inevitable rotating 3D cube.

The library’s standard example shows nearly all of TVout’s functions in action, so it’s a good idea to have a look at the code. Still, the best way to learn how to use the library is to write your own code. In the next section, you’ll create a graphical thermometer that displays the current temperature on your TV screen.

Building a TV Thermometer

To build our TV thermometer, we’ll use the TMP36 sensor again, so we’ll combine the circuit we created in Increasing Precision Using a Temperature Sensor, with the circuit we created for generating the video signal. You can see the result in Figure 24, Circuit of the TV thermometer.

images/tv_thermometer_circuit


Figure 24. Circuit of the TV thermometer

Don’t get confused by what the circuit for the video signal looks like in the circuit diagram. That’s how you would build the circuit on a breadboard. Of course, you can still connect the modified RCA cable directly to the Arduino. Then connect the circuit for the TMP36 sensor.

Before we dive into the project’s code, have a look at what we’re trying to build.

images/tv_thermometer

On the left side of the screen, you see a graphical representation of a typical thermometer. It has a scale ranging from 5.5 to 40 degrees Celsius. The thermometer isn’t a static image. The bar in the middle will grow or shrink depending on the current temperature.

On the right side of the screen, we display the current temperature as text. All in all, we have to output some text, and we have to draw some graphics. It’s a perfect opportunity to meet TVout’s most important functions, so let’s see how it works.

Video/TvThermometer/TvThermometer.ino

#include <TVout.h>

#include <fontALL.h>

#include "thermometer.h"

const float SUPPLY_VOLTAGE = 5.0;

const float MIN_TEMP = 5.5;

const float MAX_TEMP = 40.0;

const unsigned int SCREEN_WIDTH = 120;

const unsigned int SCREEN_HEIGHT = 96;

const unsigned int TEMP_SENSOR_PIN = A0;

const unsigned int SCALE_X_MIN = 8;

const unsigned int SCALE_Y_MIN = 6;

const unsigned int SCALE_Y_MAX = 75;

const unsigned int SCALE_WIDTH = 3;

const unsigned int SCALE_HEIGHT = SCALE_Y_MAX - SCALE_Y_MIN;

float current_temperature = 0.0;

unsigned long last_measurement = millis();

TVout TV;

At the beginning of our program, we include a few header files. TVout.h declares TVout’s main class and all of its methods. fontALL.h contains the definition of all fonts that TVout offers. If you don’t want to output any text, you don’t have to include it. The thermometer.h file makes the graphical representation of our thermometer available to our program. I’ll explain its content later.

After we’ve included all necessary header files, we define a few constants and variables:

· SUPPLY_VOLTAGE defines the Arduino’s supply voltage, and TEMP_SENSOR_PIN contains the number of the pin to which you’ve connected the TMP36 sensor.

· MIN_TEMP and MAX_TEMP define the minimum and maximum temperatures (in degrees Celsius) that the TV thermometer can display.

· SCREEN_WIDTH and SCREEN_HEIGHT define the screen’s width and height. Note that the Arduino isn’t capable of displaying really big screen resolutions. A width of 120 to 132 pixels and a height of 96 pixels is a reasonable compromise.

· SCALE_X_MIN defines the minimum X position of the thermometer’s scale. SCALE_Y_MIN and SCALE_Y_MAX define the minimum and maximum Y positions of the thermometer’s scale. We’ll need these constants to draw a rectangle representing the current temperature later.

· SCALE_WIDTH and SCALE_HEIGHT define the width and height of the thermometer’s scale.

· The variable current_temperature holds the last temperature we measured. last_measurement contains the time stamp in milliseconds when we last measured the current temperature. We need this because we don’t want to measure the temperature permanently, but only every few seconds.

· TV is an instance of the TVout class, and we’ll use it for accessing the TV screen using the Arduino.

Next we define the setup function:

Video/TvThermometer/TvThermometer.ino

void setup() {

TV.begin(PAL, SCREEN_WIDTH, SCREEN_HEIGHT);

TV.bitmap(0, 1, thermometer);

TV.select_font(font4x6);

TV.set_cursor(20, 4);

TV.print("40");

TV.set_cursor(20, 24);

TV.print("30");

TV.set_cursor(20, 44);

TV.print("20");

TV.set_cursor(20, 64);

TV.print("10");

}

We call the begin method of the TV object. We set the analog TV standard to PAL, and we set the screen’s width and height. If your TV set prefers NTSC, you have to replace the first argument with NTSC. After that, we invoke the bitmap method to draw the thermometer image to the screen. Its X position is 0, and its Y position is 1.

The thermometer’s image doesn’t contain numbers next to its scale, so we add the numbers in our program. Therefore, we have to select the font we’re going to use by using the select_font method. TVout’s font capabilities are quite impressive. It comes with three fixed-width fonts (4x6, 6x8, and 8x8 pixels) that are sufficient for most purposes. It also supports variable-width fonts, and it even allows you to define your own. Here we use a font that’s 4 pixels wide and 6 pixels high. The set_cursor method moves the cursor to a certain screen position, and print prints text to the current cursor position.

The loop function implements the rest of our thermometer’s business logic.

Video/TvThermometer/TvThermometer.ino

Line 1

void loop() {

-

unsigned long current_millis = millis();

-

if (abs(current_millis - last_measurement) >= 1000) {

-

current_temperature = get_temperature();

5

last_measurement = current_millis;

-

int y_pos = mapfloat(

-

current_temperature, MIN_TEMP, MAX_TEMP, SCALE_Y_MAX, SCALE_Y_MIN);

-

TV.draw_rect(

-

SCALE_X_MIN, SCALE_Y_MIN, SCALE_WIDTH, SCALE_HEIGHT, BLACK, BLACK);

10

TV.draw_rect(

-

SCALE_X_MIN, y_pos, SCALE_WIDTH, SCALE_Y_MAX - y_pos, WHITE, WHITE);

-

TV.select_font(font6x8);

-

TV.set_cursor(53, 1);

-

TV.print("Current");

15

TV.set_cursor(40, 11);

-

TV.print("Temperature:");

-

TV.select_font(font8x8);

-

TV.set_cursor(50, 25);

-

TV.print(current_temperature, 1);

20

TV.print(" C");

-

TV.draw_circle(88, 27, 1, WHITE);

-

}

-

}

-

25

const float mapfloat(

-

float x, float in_min, float in_max, float out_min, float out_max)

-

{

-

return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;

-

}

30

-

const float get_temperature() {

-

const int sensor_voltage = analogRead(TEMP_SENSOR_PIN);

-

const float voltage = sensor_voltage * SUPPLY_VOLTAGE / 1024;

-

return (voltage * 1000 - 500) / 10;

35

}

We make sure that we measure the current temperature only once per second. Whenever we determine the current temperature, we calculate the new upper Y position of the thermometer’s scale in line 6. The map_float function maps the current temperature to a value on our thermometer’s scale.

Then we use the draw_rect method (which draws a rectangle on the screen) twice. The first call erases the thermometer’s scale completely. This is necessary because the temperature can rise or fall. We could clear and redraw the whole screen every time, but that would be overkill. The second call draws a white rectangle on our scale that represents the current temperature.

Next, we output the current temperature as text. We use TVout’s select_font, set_cursor, and print methods to output the text “Current Temperature” in a font that is 6 pixels wide and 8 pixels high. After that, we output the current temperature in degrees Celsius using the 8x8 font. The TVout library doesn’t define a symbol for degrees Celsius, so we use the draw_circle method in line 21 to draw a small circle to simulate a degrees symbol.

We’re done! That’s all the code we need to make the TV thermometer work. The only thing I haven’t explained in detail is how outputting the thermometer image works. You’ll learn more about that in the next section.

Working with Graphics in TVout

In TvThermometer.ino we’ve included the thermometer.h file without explaining what it contains. Here’s how it looks:

Video/TvThermometer/thermometer.h

#ifndef THERMOMETER_H

#define THERMOMETER_H

extern const unsigned char thermometer[];

#endif

Quite disappointing, isn’t it? The file declares only a single variable named thermometer. This variable is an array of unsigned character values, and the extern keyword tells the compiler that we only want to declare the variable. That is, we can refer to it in our program, but we still have to define it to allocate some memory.

We actually define the thermometer variable in thermometer.cpp (we’ve skipped a few lines for brevity):

Video/TvThermometer/thermometer.cpp

Line 1

#include <Arduino.h>

-

#include <avr/pgmspace.h>

-

#include "thermometer.h"

-

PROGMEM const unsigned char thermometer[] = {

5

20, 94,

-

B00000000, B11110000, B00000000,

-

B00000001, B00001000, B00000000,

-

B00000010, B00000100, B00000000,

-

B00000010, B00000100, B00000000,

10

B00000010, B00000100, B00000000,

-

B00000010, B00000111, B10000000, // 40.0

-

B00000010, B00000100, B00000000,

-

B00000010, B00000100, B00000000,

-

B00000010, B00000100, B00000000,

15

B00000010, B00000100, B00000000,

-

B00000010, B00000100, B00000000,

-

B00000010, B00000100, B00000000,

-

B00000010, B00000100, B00000000,

-

B00000010, B00000100, B00000000,

20

B00000010, B00000100, B00000000,

-

// ...

-

B00000010, B00000100, B00000000, // 5.5

-

B00000111, B11111110, B00000000,

-

B00001111, B11111111, B00000000,

25

B00011111, B11111111, B10000000,

-

B00111111, B11111111, B11000000,

-

B01111111, B11111111, B11100000,

-

B01111111, B11111111, B11100000,

-

B11111111, B11111111, B11110000,

30

B11111111, B11111111, B11110000,

-

B11111111, B11111111, B11110000,

-

B11111111, B11111111, B11110000,

-

B11111111, B11111111, B11110000,

-

B11111111, B11111111, B11110000,

35

B01111111, B11111111, B11100000,

-

B01111111, B11111111, B11100000,

-

B00111111, B11111111, B11000000,

-

B00011111, B11111111, B10000000,

-

B00001111, B11111111, B00000000,

40

B00000111, B11111110, B00000000,

-

B00000001, B11111000, B00000000,

-

B00000001, B11111000, B00000000,

-

};

This file looks weird at first, but it’s really simple. First, we include Arduino.h because we’ll need to declare binary constants later. After that, we include avr/pgmspace.h because we want to store our image data in the Arduino’s flash RAM. Eventually, we include thermometer.h because we need the declaration of our thermometer image data.

In line 4, we eventually define the thermometer variable we declared in thermometer.h. The definition differs slightly from the declaration because it contains the PROGMEM directive.[81] This directive tells the compiler to copy the data stored in the thermometer variable to the Arduino’s flash memory. Usually, when you define a variable in an Arduino program, it occupies memory in the SRAM. Most Arduinos don’t have a lot of SRAM (the Arduino Uno only has 2 KB), so it’s a valuable resource and you shouldn’t waste it. As a rule of thumb, you should store all constant data in the Arduino’s flash RAM. Use SRAM only for information that might change during program execution.

Image data like our thermometer usually doesn’t change, so you should always store it in flash RAM using the PROGMEM directive. TVout expects image data in raw format. The first two bytes contain the width and height of an image. The data that follows contains the image data line by line. In thermometer.cpp, each line of image data contains three bytes, because the image is 20 pixels wide, and 20 pixels occupy three bytes. Consequently, the file contains 94 lines each representing a single line of the thermometer image. Because we’ve used binary literals to encode the image data, you can actually see how the image looks when reading the source code. A 1 represents a white pixel, and a 0 represents a black pixel.

Drawing Images for Your Arduino Programs

You can draw simple images directly in the source code by editing binary numbers. As soon as your images get more complex, you need some tool support. For graphics that are still fairly simple but that are too complex to edit the binary numbers in the source code, you can use any drawing program, of course, but most modern tools are way too complicated for this job.

I’ve created the thermometer image with a fairly simple online tool named Piskel.[82] It’s open source, it’s easy to use, and it feels just right for creating Arduino graphics. You can see it in action in Figure 25, You can find good online editors for pixel graphics.

images/piskel


Figure 25. You can find good online editors for pixel graphics.

Applications like Piskel really help to create images for your Arduino programs, but they usually store these images in .gif or .png files. In the next section, you’ll learn how to convert these files into source code.

Turning Pixels into C++ Code

After you’ve finished your pixel art, you still have to convert it into a C/C++ file. You could do it manually, but that wouldn’t be very pragmatic, would it? It’d be much better to write a small program that does the conversion automatically.

You could write such a program in any modern programming language; we’ll use Ruby here. Processing graphics in Ruby is easy thanks to the rmagick library. This library is a binding to ImageMagick,[83] a powerful tool for transforming images. Before you can install rmagick, you have to install ImageMagick.

When ImageMagick is available on your system, you can install the rmagick library using the following command:

maik> gem install rmagick

Now you can use rmagick in your Ruby programs. We’ll use it to convert a graphics file into a C++ file:

Video/img2cpp.rb

Line 1

require 'RMagick'

-

include Magick

-

-

image = Image::read(ARGV[0]).first

5

-

puts '#include "thermometer.h"'

-

puts 'PROGMEM const unsigned char thermometer[] = {'

-

puts " #{image.columns}, #{image.rows},"

-

10

(0..image.rows).each do |y|

-

print ' B'

-

(0..image.columns).each do |x|

-

pixel = image.pixel_color(x, y)

-

print pixel.red == 0 ? '0' : '1'

15

print ', B' if (x + 1) % 8 == 0

-

end

-

print '0' * (8 - (image.columns % 8))

-

puts ','

-

end

20

-

puts '};'

First, the program loads the rmagick library and imports the RMagick namespace. We do this to save some typing, because now we don’t have to fully qualify all classes that live in the RMagick namespace. In line 4, we read an image whose name we have to pass as a command-line argument. The image file’s format doesn’t matter, because ImageMagick understands nearly all image file formats. The image variable contains a representation of the image that doesn’t depend on the original file format any longer.

Next, we output the first three lines of the C++ file we’d like to generate. These lines are mostly static. Only the third line contains some variable parts—that is, the image’s width and the height.

Then we process the image’s pixels using two nested loops. The outer loop iterates through each row of the image, and the inner loop through each column. In line 13 we read the current pixel, and in the next line we use a cheap trick to determine whether the pixel is black or white. We know that our images consist only of black and white pixels, so it’s sufficient to check only one color component. If the red component is 0, the pixel has to be black. If it’s 1, the pixel has to be white. We transform every pixel into a bit value, and if the number of pixels in an image row isn’t divisible by 8 without a remainder, we fill the remaining bits with zeros.

You can run the program like this:

maik> ruby img2cpp.rb thermometer.png > thermometer.cpp

This call turns the thermometer.png file into a C++ file you can add to your Arduino project without any further modifications. That’s how software developers approach boring and error-prone tasks.

In the next chapter, you’ll learn how to connect a Wii Nunchuk to your Arduino, and we’ll use the TVout library to turn the Arduino into a video game console.

What If It Doesn’t Work?

Even if this chapter’s hardware is simple, a few things can still go wrong. If you don’t see a video signal at all on your TV set, make sure you’ve selected the right input source. Usually its name is AV or Composite. When in doubt, try all of them.

Then check whether you’ve swapped the resistors. Connect the 470Ω resistor to pin D7 and the 1kΩ resistor to D9. Also, make sure the resistors have the right values.

If you see a distorted video signal, make sure you haven’t accidentally used NTSC instead of PAL or vice versa. Also, check all solder joints on the modified RCA cable. When in doubt, add more solder.

Exercises

· Use the TVout library to visualize some other sensor’s data. You can try to build the parking-distance control from Chapter 5, Sensing the World Around Us, using the TVout library.

· Modify the TV thermometer so that it switches between degrees Celsius and degrees Fahrenheit every few seconds. Don’t just change the text; change the graphics, too.

Footnotes

[77]

http://en.wikipedia.org/wiki/Digital-to-analog_converter

[78]

http://en.wikipedia.org/wiki/Voltage_divider

[79]

https://code.google.com/p/arduino-tvout/

[80]

https://arduino-tvout.googlecode.com/files/TVoutBeta1.zip

[81]

http://arduino.cc/en/Reference/PROGMEM

[82]

http://www.piskelapp.com/

[83]

http://www.imagemagick.org/