How to use Arduino TVout library to build a thermometer that displays temperature on TV.
Analog video is getting replaced by digital signals which provide better resolution and picture without noise or interference. Although receivers for digital signals are cheap and popular, devices for generating such signals are expensive and intended for professional use only. On the other hand, analog video is easy to generate with simple hardware. You can even broadcast it over RF (on wire, not on air) with common modulators (standalone devices or modules from video game consoles, set top boxes, VCRs etc.).
An easy way to generate video signal is by using a microcontroller and some resistors. I'll use for this purpose an Arduino board (ATmega 328p) with the TVout library. The video signal is of low resolution and black&white. But it can be used to display data on a TV screen. If you no longer own a TV with analog video input, an USB capture card can be used. TVout library is interrupt based, therefore will interfere with some of other interrupt dependent microcontroller features.
For example, using TVout, the temperature readout from a sensor can be displayed. But not all kind of sensors. Because TVout is interrupt based, you can't use OneWire or I2C sensors. OneWire communication interferes with video sync, therefore every time the sensor is read, picture get distorted for a short time. I2C however is completely incompatible with TVout (you get no picture at all once you use Wire library).
All that's left is to use an analog sensor (thermistor). These are good too, but require calibration. Here is the schematic of the device.
Thermometer with TVout schematic
The schematic is very simple. I built it on a breadboard. If you don't have the KY-013 module, you can build the voltage divider with a thermistor and 100 ohms resistor.
TVout thermometer built on breadboard
I used an audio jack on the breadboard (one channel is for video, the other for audio). A jack to RCA cable is plugged into this.
The thermometer displays the actual temperature, the minimum and the maximum and a small graph of temperature variation over 116 seconds. When a new minimum or maximum value is recorded, a short beep of 1kHz is generated.
TVout library is required. To install it, use the Library Manager of Arduino IDE. However, compilation will fail unless you find the library folder (where your projects are, there is a libraries folder; find TVout, enter it) where you must delete TVout subfolder and rename TVOutfonts to utility. The issue has been reported on GitHub. At the end of this post you can download a ready modified version that compiles without errors.
Here is the code:
#include <TVout.h> #include <utility/fontALL.h> TVout TV; float minTemp = 100, maxTemp = 0; byte graph[117] = { 0 }; // Read thermistor and convert to Celsius degrees // https://tkkrlab.nl/wiki/Arduino_KY-013_Temperature_sensor_module double readThermistor(int RawADC) { double Temp; Temp = log(10000.0 * ((1024.0 / RawADC - 1))); Temp = 1 / (0.001129148 + (0.000234125 + (0.0000000876741 * Temp * Temp )) * Temp ); Temp = Temp - 273.15; // Convert Kelvin to Celcius //Temp = (Temp * 9.0)/ 5.0 + 32.0; // Convert Celcius to Fahrenheit return Temp; } void draw_initial_screen() { TV.clear_screen(); TV.set_cursor(19, 3); TV.select_font(font8x8); TV.print("Thermometer"); TV.draw_rect(0, 0, 125, 14, 1, 2); TV.select_font(font6x8); TV.set_cursor(0, 20); TV.print("MIN"); TV.set_cursor(43, 20); TV.print("Current"); TV.set_cursor(109, 20); TV.print("MAX"); TV.select_font(font4x6); TV.set_cursor(4, 86); TV.print("0"); TV.set_cursor(0, 76); TV.print("10"); TV.set_cursor(0, 66); TV.print("20"); TV.set_cursor(0, 56); TV.print("30"); TV.set_cursor(0, 46); TV.print("40"); TV.draw_line(10, 46, 10, 88, 1); TV.draw_line(10, 88, 126, 88, 1); TV.set_pixel(9, 48, 1); TV.set_pixel(11, 48, 1); TV.set_pixel(9, 58, 1); TV.set_pixel(11, 58, 1); TV.set_pixel(9, 68, 1); TV.set_pixel(11, 68, 1); TV.set_pixel(9, 78, 1); TV.set_pixel(11, 78, 1); TV.set_cursor(67, 90); TV.select_font(font4x6); TV.print("2 min evolution"); } void display_min(float mt) { TV.set_cursor(0, 33); TV.select_font(font6x8); if (mt < 10) TV.print(" "); TV.print(mt, 1); } void display_max(float mt) { TV.set_cursor(103, 33); TV.select_font(font6x8); if (mt < 10) TV.print(" "); TV.print(mt, 1); } void display_current(float t) { TV.set_cursor(42, 32); TV.select_font(font8x8); TV.print(t, 2); TV.draw_rect(40, 30, 44, 12, 1, -1); } void setup() { TV.begin(PAL, 128, 96); draw_initial_screen(); delay(1000); } void display_graph(float ct) { for (int i = 0; i < 116; i++) { graph[i] = graph[i + 1]; TV.set_pixel(i + 10, 88 - graph[i], 1); if (graph[i + 1] > 0) TV.set_pixel(i + 11, 88 - graph[i + 1], 0); } graph[116] = (byte)ct; if (graph[116] > 40) graph[116] = 40; } void loop() { float currTemp = readThermistor(analogRead(A2)); display_current(currTemp); display_graph(currTemp); if (currTemp < minTemp) { minTemp = currTemp; display_min(minTemp); TV.tone(1000, 200); } if (currTemp > maxTemp) { maxTemp = currTemp; display_max(maxTemp); TV.tone(1000, 200); } delay(1000); }
Analog video signal generation with an Arduino takes up a lot of the microcontroller resources. However digitalRead()
and digitalWrite()
, as well as analogRead()
do work. This lets you interface the microcontroller with various analog sensors and to trigger relays or other devices. I haven't tried this yet, but I guess it's possible to use digital type sensors (DHT11/DHT22/AM2302) which can be read without using interrupts.
Going further from here, you can connect AV outputs to a RF modulator. Remember you can't use an I2C controlled modulator because the Arduino used for video generation can't communicate via I2C too. You can use a fixed output channel modulator from a game console. And do not output RF signal into an antenna. Although analog TV broadcasts have been shut off, it is probably illegal to broadcast on that frequency bands. Use a cable to send the signal over a long distance.
Resources
- TVout library, modified to build the sketch without issues
- Complete sketch (GitHub)
- The sensor kit which contains the module used in this project
Helpful info. Lucky me I discovered your website unintentionally, and I'm
ReplyDeletesurprised why this twist of fate didn't happened earlier!
I bookmarked it.
Hello.
ReplyDeletePlease tell me, why it does not work for Arduino Leonardo?
On the TV, I normally see the "draw_initial_screen" function, but the ADC data is not displayed.
It should work. Try a different ADC pin and check wire connections.
DeleteFirst versions of TVout did not cover Leonardo, try this one: https://github.com/Avamander/arduino-tvout
DeleteAnd... for Arduino Leonardo try to connect the 470 resistor on D8
After replacing in void setup and void loop the function delay(1000); with delayMicroseconds(1000000); everything worked, but the update speed is very high.
DeleteA "delay()" stops the program.
(Sorry for my English.)
The problem is closed! ))
ReplyDeleteNeed to replace the "delay(1000);" on the "TV.delay(1000);".
Thank you all for your help.
Cornelius, nice code.
I guess that's the right way to do it. I'll update the code with TV.delay(). Thank you!
DeleteHi, there a way to set pixel color?
ReplyDeleteNo, this is monochrome only.
Delete