The ATtiny13 is a low-power CMOS 8-bit microcontroller based on the AVR enhanced RISC architecture. By executing powerful instructions in a single clock cycle, the ATtiny13 achieves throughputs approaching 1 MIPS per MHz allowing the system designer to optimize power consumption versus processing speed.
To program the ATTiny, it is quite straightforward to set up an Arduino board as an AVRISP (AVR is a family of microcontrollers and ISP means In System Programmer). Firstly, the Arduino IDE needs to be configured. The Arduino IDE can be download from here: https://www.arduino.cc/en/main/software
Once installed, the first
thing to do is to turn the Arduino into an ISP. Open the Arduino IDE and select
File -> Examples -> 11.ArduinoISP ->ArduinoISP and upload this to an
Arduino board.
https://mcudude.github.io/MicroCore/package_MCUdude_MicroCore_index.json
Click “OK” to save the
settings.
Next, open Tools -> Board -> Boards manager. Find MicroCore and click OK to install it.
Now connect the ATTiny 13A
to the Arduino. Connect the pins as follows:
ATTiny13A Arduino
pin 1 pin 10
pin 5 pin 11
pin 6 pin 12
pin 7 pin 13
pin 8 5v
pin 4 Ground (GND)
Here is a schematic of the
ATTiny 13A chip pinouts.
You will find
it easier to connect the ATTiny pins to the Arduino pins via a breadboard. To
set up the ATTiny in the Arduino IDE, select “Tools” and select the following:
Board: ATtiny
13
BOD: 2.7v
Clock: 9.6 MHz
internal osc.
Compiler LTO:
Enable
Port: (Your
Arduino port)
Programmer:
Arduino as ISP
Finally, you
will need to burn Bootloader ((You only need to do this once per chip)
In the Arduino
IDE, select “Tools”, “Burn Bootloader”.You might see an error, but ignore it.
The ATTiny is
now ready to receive programs via the Arduino. This can be done by
writing/compiling the program (sketch) in the Arduino IDE and then selecting
“Sketch”, “Upload Using Programmer”. There follows a series of experiments with
the ATTiny (ATTiny 13A is used throughout) that will illustrate some of it’s
capabilities. These can be adapted and incorporated into a huge range of
applications. The ATTiny will needed to be connected to the Arduino ISP to enable
it to receive each program (sketch). Note that this setup, or “core” is used in
all the examples up to the section “Serial Communication”. That section
describes the setting up of a different core. The subsequent examples that
appear use that new core.
Below is a list of links to various topics and applications.
Core 1
Blink LED
Fade LED 20
Traffic Light Display Module 21
RGB LED 22
Basic Buzzer 25
Buzzer Tone Generator 25
Piezoelectric Speaker 28
Tilt Sensor 31
PIR Motion Detector 32
Reed Sensor 33
Hall Effect Sensor 34
Capacitive Touch Sensor 35
Push Button Switch 36
Stepper Motor with ULN2003 Driver 38
Shift Register 74HC595N 40
Line Decoder SN74HCT138N 47
Core 2
Serial Communication 52
Serial Menu 53
Read Serial ASCII String 55
Serial Digital Clock 56
Random Number Generator 58
Physical Pixel 59
Randomly flashing LED with PRNG 60
Temperature Sensor 62
TM1637 Seven Segment Display 63
TM1637 Seven Segment Display With Library. 72
DHT-11 Temperature and Humidity Sensor 73
Light Sensor 76
Sound Sensor 78
Flame Detection Sensor 80
Soil Moisture Sensor 82
Ultrasound Distance Measurement 83
Potentiometer 86
Data Logging: MakerPlot 87
NE555 Frequency Adjustable Pulse Generator 90
LM386 Mono Class D Amplifier 92
Nokia 5110 LCD Display 94
Blink LED
This example will instruct
the ATTiny to turn an LED on and off at a pre-determined frequency.
Add an LED to pin 3 (PB4)
of the ATTiny. The longer leg of the LED to this pin and the shorter leg to
Ground (GND). Copy the following sketch and paste it into the Arduino IDE.
Select “Sketch”, “Upload Using Programmer”.
void setup() {
// initialize pin 3 (ATtiny PB4, leg 3) as an
output.
pinMode(4, OUTPUT);
}
void loop() {
digitalWrite(4, HIGH); // turn the LED on (HIGH is the voltage
level)
delay(1000); // wait for a second
digitalWrite(4, LOW); // turn the LED off by making the voltage
LOW
delay(1000); // wait for a second
}
The LED should turn off and
on every 1000 milliseconds (1 second). The program is easily adapted to alter
the frequency of the turning the LED on and off. This is done by simply
changing the
Fade LED
Rather than just turn an
LED on and off, this example will instruct the ATTiny to fade an LED on and off
at a pre-determined frequency. Attach an LED to PB0 pin, leg 5 of the Attiny, the longer leg
of the LED to this pin and the shorter leg to Ground (GND). Upload the
following program:
#include <avr/io.h>
#include <util/delay.h>
// initialize ATtiny PB0 pin, (leg
5) as an output
#define LED_PIN PB0
//#define LED_PIN 4
#define DELAY_MAX (512)
#define DELAY_MIN (1)
#if DELAY_MAX < 1 ||
DELAY_MIN < 1
# warning "Value of
DELAY_MAX and DELAY_MAIN should be from range <1, 2^16>"
#endif
#if !(DELAY_MAX >
DELAY_MIN)
# warning "Value of
DELAY_MAX should be greater then DELAY_MIN"
#endif
int
main(void)
{
uint16_t delay = DELAY_MIN;
uint8_t dir = 0;
/* setup */
DDRB = 0b00000001; // set LED pin as OUTPUT
PORTB = 0b00000001; // set LED pin to
HIGH
/* loop */
while (1) {
PORTB &= ~(_BV(LED_PIN));
// LED off
_delay_loop_2(delay);
PORTB |= _BV(LED_PIN); // LED
on
_delay_loop_2(DELAY_MAX -
delay);
if (dir) { // fade-in
if (++delay >=
(DELAY_MAX - 1)) dir = 0;
} else { // fade-out
if (--delay <= DELAY_MIN)
dir = 1;
}
}
}
You should see the LED fadie
between fully on and fully off.
Traffic Light
Display Module
These
small modules are designed for inclusion in model projects. As the name
suggests, they can be used in projects and simulations involving traffic
lights.
The
module consists of three LEDs; red, yellow and green. You will need to connect
these up to separate pins on the ATTiny so that you can make them turn on and
off individually. In the example below, I have connected the LED pins as
follows:
Green
LED ATTiny pin 5 (PB0)
Yellow
LED ATTiny pin 2 (PB3)
Red
LED ATTiny pin 3 (PB4)
We
need to decide on the sequence that the LEDs should turn on and off. This
example scrolls through the LEDs in the following sequence: green, amber, red.
Connect
the module to the ATTiny as described above and upload the following sketch:
void
setup() {
// initialize pin 0 (ATtiny leg 3) as an
output.
pinMode(0, OUTPUT); //Green
pinMode(3, OUTPUT); //Amber
pinMode(4, OUTPUT); //Red
}
void
loop() {
digitalWrite(0, HIGH); // turn the LED on (HIGH is the voltage
level)
delay(1000); // wait for a second
digitalWrite(0, LOW); // turn the LED off by making the voltage
LOW
digitalWrite(3, HIGH); // turn the LED on (HIGH is the voltage
level)
delay(1000); // wait for a second
digitalWrite(3, LOW); // turn the LED off by making the voltage
LOW
digitalWrite(4, HIGH); // turn the LED on (HIGH is the voltage
level)
delay(1000); // wait for a second
digitalWrite(4, LOW); // turn the LED off by making the voltage
LOW
}
You
will see that the sequence is continuously followed and, as the sketch is a
loop, the sequence will just continue to run and run.
By
putting pauses (delay commands) between each step of the sequence, you can
achieve a pretty good representation of real traffic lights.
RGB LED
The
red, green, blue (RGB) LED has the ability to show a multitude of colours. As
the name suggests, it can be red, green or blue, or any combination of these
colours. This makes it very useful if we want to show different colours by
using just one LED or show an unusual colour (not red, green, or blue). This
type of LED has four pins; one for each of the three colours and one that is ground.
You
will need to connect these module pins up to separate pins on the ATTiny so
that you can make them turn on and off individually. In the example below, I
have connected the LED pins as follows:
Green
pin ATTiny pin 5 (PB0)
Blue
pin ATTiny pin 2 (PB3)
Red
pin ATTiny pin 3 (PB4)
GND (Ground) ATTiny pin 4
Connect
the module to the ATTiny as described and upload the following sketch:
void
setup() {
// initialize pin 0 (ATtiny leg 3) as an
output.
pinMode(0, OUTPUT); //Green
pinMode(3, OUTPUT); //Blue
pinMode(4, OUTPUT); //Red
}
void
loop() {
digitalWrite(0, HIGH); // turn the LED on (HIGH is the voltage
level)
delay(1000); // wait for a second
digitalWrite(0, LOW); // turn the LED off by making the voltage
LOW
digitalWrite(3, HIGH); // turn the LED on (HIGH is the voltage
level)
delay(1000); // wait for a second
digitalWrite(3, LOW); // turn the LED off by making the voltage
LOW
digitalWrite(4, HIGH); // turn the LED on (HIGH is the voltage
level)
delay(1000); // wait for a second
digitalWrite(4, LOW); // turn the LED off by making the voltage
LOW
}
As
with the earlier example of the Traffic Light Module, you will see a sequence
of scrolling through the individual colours (red, green, blue).
There is a way of fading
between colours with this module. The connections between the RGB module and
the ATtiny are as follows:
Red
pin ATTiny pin 5 (PB0)
Green
pin ATTiny pin 6 (PB1)
Blue
pin ATTiny pin 7 (PB2)
GND (Ground) ATTiny pin 4
Upload the following program to
the ATTiny:
#include <avr/io.h>
#include <util/delay.h>
/* LED RBG pins */
#define LED_RED
PB0
#define LED_GREEN
PB1
#define LED_BLUE
PB2
/* Rainbow settings */
#define MAX
(512)
#define STEP
(4)
/* Fading states */
#define REDtoYELLOW (0)
#define YELLOWtoGREEN (1)
#define GREENtoCYAN (2)
#define CYANtoBLUE (3)
#define BLUEtoVIOLET (4)
#define VIOLETtoRED (5)
/* Global variables */
uint16_t red = MAX;
uint16_t green = 0;
uint16_t blue = 0;
uint16_t state = 0;
void
rainbow(int n)
{
switch (state) {
case REDtoYELLOW: green += n; break;
case YELLOWtoGREEN: red -= n; break;
case GREENtoCYAN: blue += n; break;
case CYANtoBLUE: green -= n; break;
case BLUEtoVIOLET: red += n; break;
case VIOLETtoRED: blue -= n; break;
default: break;
}
if (red >= MAX || green >= MAX ||
blue >= MAX || red <= 0 || green <= 0 || blue <= 0) {
state = (state + 1) % 6; // Finished
fading a color so move on to the next
}
}
int
main(void)
{
uint16_t i = 0;
/* --- setup --- */
DDRB = 0b00000111;
PORTB = 0b00000111;
/* --- loop --- */
while (1) {
/* Rainbow algorithm */
if (i < red) {
PORTB &= ~(1 << LED_RED);
} else {
PORTB |= 1 << LED_RED;
}
if (i < green) {
PORTB &= ~(1 <<
LED_GREEN);
} else {
PORTB |= 1 << LED_GREEN;
}
if (i < blue) {
PORTB &= ~(1 <<
LED_BLUE);
} else {
PORTB |= 1 << LED_BLUE;
}
if (i >= MAX) {
rainbow(STEP);
i = 0;
}
i++;
}
return (0);
}
Basic Buzzer
This example will instruct
the ATTiny to turn buzzer on and off at a pre-determined frequency, in this
case every second.
Add a buzzer to pin 5 (PB0)
of the ATTiny, one leg of the buzzer to this pin and other to Ground (GND).
Copy the following sketch and paste it into the Arduino IDE. Select “sketch”,
“Upload Using Programmer”.
void setup() {
// initialize pin 0 (PB0, ATtiny leg 5) as an
output.
pinMode(0, OUTPUT);
}
void loop() {
digitalWrite(0, HIGH); // turn the BUZZER on (HIGH is the voltage
level)
delay(1000); // wait for a second
digitalWrite(0, LOW); // turn the BUZZER off by making the
voltage LOW
delay(1000); // wait for a second
}
The frequency of the
turning the buzzer on and off can be altered by changing the value of the
Buzzer Tone Generator
The following example
generates a tone and is reproduced here by permission of it’s author, Łukasz Marcin
Podkalicki.
/**
* Copyright (c) 2016, Łukasz Marcin Podkalicki
<lpodkalicki@gmail.com>
* ATtiny13/015
* Two-Tone Alarm.
* --
* Settings:
*
FUSE_L=0x6A
*
FUSE_H=0xFF
*
F_CPU=1200000
*/
#include <avr/io.h>
#include
<avr/interrupt.h>
#include
<avr/pgmspace.h>
#include <util/delay.h>
#define SPEAKER_PIN
PB0
#define N_1
(_BV(CS00))
#define N_8
(_BV(CS01))
#define N_64
(_BV(CS01)|_BV(CS00))
#define N_256
(_BV(CS02))
#define N_1024 (_BV(CS02)|_BV(CS00))
static void
twotone_alarm(uint8_t type);
static void tone_loop(uint8_t
OCRxn, uint8_t N, uint8_t max, uint8_t delay, uint8_t pause, uint8_t fade);
static void timer_set(uint8_t
OCRxn, uint8_t N);
static void sleep(uint8_t ms);
int
main(void)
{
/* setup */
DDRB |= _BV(SPEAKER_PIN); // set speaker
pin as OUTPUT
TCCR0A |= _BV(WGM01); // set timer mode to
Fast PWM
TCCR0A |= _BV(COM0A0); // connect PWM pin
to Channel A of Timer0
/* loop */
while (1) {
twotone_alarm(1);
}
}
void
twotone_alarm(uint8_t type)
{
switch(type) {
/* Please, put here your own two-tone alarm
composition! */
case 1:
tone_loop(123, N_8, 6, 10, 10, 1);
tone_loop(22, N_8, 6, 10, 0, -1);
break;
default:
case 0:
tone_loop(32, N_8, 6, 10, 10, 1);
tone_loop(22, N_8, 6, 10, 0, -1);
break;
}
}
/**
* Single tone loop with fade-in/out effect.
*
* Base square wave frequency,
* F = F_CPU / (2 * N * (1 + OCRnx)), where:
* - F is a calculated PWM frequency
* - F_CPU is a clock source (1.2MHz)
* - the N variable represents the prescaler
factor (1, 8, 64, 256, or 1024)
*
* @param OCRxn: timer OCRxn value
* @param N: timer prescaler (N_1, N_8, N_64,
N_256, N_1024)
* @param max: number of iterations (incr/decr
of OCRxn)
* @param delay: little delay after each iteration
in miliseconds
* @param pause: delay after a tone loop, delay
between tones
* @param fade: fade-in (1) or fade-out (-1)
factor
*/
void
tone_loop(uint8_t OCRxn,
uint8_t N, uint8_t max, uint8_t delay, uint8_t pause, uint8_t fade)
{
uint8_t i;
for (i = 0; i < max; ++i) {
timer_set(OCRxn, N);
OCRxn += fade;
sleep(delay);
}
sleep(pause);
}
void
timer_set(uint8_t OCRxn,
uint8_t N)
{
TCCR0B = (TCCR0B &
~((1<<CS02)|(1<<CS01)|(1<<CS00))) | N;
OCR0A = OCRxn - 1;
}
void
sleep(uint8_t ms)
{
uint8_t i;
for (i = 0; i < ms; ++i) {
_delay_ms(1);
}
}
Piezoelectric
Speaker
A
piezoelectric speaker can play, as well as detect tones and can, with a little
help, enable us to play actual melodies. By using the microcontroller’s
capability of producing pulse width modulation (PWM) signals, we are able to
alter the pitch of the sound. If you need to know more about PWM, details can
be found here: http://webzone.k3.mah.se/k3dacu/projects/ivrea/motor/pwm.html
Here
is a program that will play many tones (the same program that is featured
earlier for the continuous buzzer). Attach red wire to pin 5 (PB0) of the
ATTiny and the black wire to GND. Upload the following sketch using the Arduino
ISP programmer.
#include
<avr/io.h>
#include
<avr/interrupt.h>
#include
<avr/pgmspace.h>
#include
<util/delay.h>
#define SPEAKER_PIN PB0 // pin 5 of the ATTiny
#define N_1
(_BV(CS00))
#define N_8
(_BV(CS01))
#define N_64
(_BV(CS01)|_BV(CS00))
#define N_256
(_BV(CS02))
#define N_1024 (_BV(CS02)|_BV(CS00))
static
void twotone_alarm(uint8_t type);
static
void tone_loop(uint8_t OCRxn, uint8_t N, uint8_t max, uint8_t delay, uint8_t
pause, uint8_t fade);
static
void timer_set(uint8_t OCRxn, uint8_t N);
static
void sleep(uint8_t ms);
int
main(void)
{
/* setup */
DDRB |= _BV(SPEAKER_PIN); // set speaker
pin as OUTPUT
TCCR0A |= _BV(WGM01); // set timer mode to
Fast PWM
TCCR0A |= _BV(COM0A0); // connect PWM pin
to Channel A of Timer0
/* loop */
while (1) {
twotone_alarm(1);
}
}
void
twotone_alarm(uint8_t
type)
{
switch(type) {
case 1:
tone_loop(123, N_8, 6, 10, 10, 1);
tone_loop(22, N_8, 6, 10, 0, -1);
break;
default:
case 0:
tone_loop(32, N_8, 6, 10, 10, 1);
tone_loop(22, N_8, 6, 10, 0, -1);
break;
}
}
void
tone_loop(uint8_t
OCRxn, uint8_t N, uint8_t max, uint8_t delay, uint8_t pause, uint8_t fade)
{
uint8_t i;
for (i = 0; i < max; ++i) {
timer_set(OCRxn, N);
OCRxn += fade;
sleep(delay);
}
sleep(pause);
}
void
timer_set(uint8_t
OCRxn, uint8_t N)
{
TCCR0B = (TCCR0B &
~((1<<CS02)|(1<<CS01)|(1<<CS00))) | N;
OCR0A = OCRxn - 1;
}
void
sleep(uint8_t
ms)
{
uint8_t i;
for (i = 0; i < ms; ++i) {
_delay_ms(1);
}
}
By
adding various delay commands and different notes you should be able to compose
a simple melody. I’m sure that you will have hours of fun with this, whilst
also gaining valuable experience by working with the program.
Tilt Sensor
A
tilt sensor can be a useful component in numerous projects. Fixed to objects,
it switches between two states if moved or tilted. The SW-520D is a simple ball
tilt switch which has two pins. With the two pins level, a 10° tilt will close
the contacts, thus altering the “state”.
As
well as the sensor, we will also attach an LED so that we will have a visual
indication of the sensors state. Firstly connect an LED to pin PB1 and
(shortest leg) to GND Connect one of the
pins of the sensor to ground and the other pin to a 10k ohm pull up resistor
and to ATTiny pin PB3.
Upload
the following program (also available from
starter.html
):
const
int sensorPin = PB3;
const
int ledPin = PB1;
int
sensorState = 0;
void
setup() {
pinMode(ledPin,
OUTPUT); // define LED pin as output
pinMode(sensorPin,
INPUT); // define sensor pin as output
}
void
loop(){
sensorState
= digitalRead(sensorPin); // state = value of the pin
if
(sensorState == HIGH) {
digitalWrite(ledPin,
HIGH);
}
else
{digitalWrite(ledPin, LOW);}
}
When
the sensor is moved, its state will change from “LOW” to “HIGH” and the light will flash correspondingly. Move
the breadboard that is housing the tilt sensor to see this happening.
PIR motion detectors are widespread as they are
used in many security systems to detect events such as trespass or other
motion. The modules themselves are quite inexpensive and can be readily bought
from numerous sources on the internet.
They have a range of detection of about 6–7m and are
highly sensitive. When the PIR
motion sensor detects motion, it outputs a 5V signal to the microcontroller and triggers an
interrupt. The required circuit is relatively simple:
Connect
the input pin to ATTiny pin 2 (PB3), GND to GND and 5V to 5V. I have also added
an LED to pin 6 (PB1), which has also been connected to GND.
Once
the circuit is set up, construct a sketch similar to the one below ans upload
it to the ATTiny using the ISP.
int
ledPin = PB1; // the pin for the LED
int
inputPin = PB3; // input pin (for PIR sensor)
int
pirState = LOW; //
we start, assuming no motion detected
int
val = 0; // variable for reading the pin
status
void
setup() {
pinMode(ledPin, OUTPUT); //
declare LED as output
pinMode(inputPin, INPUT); //
declare sensor as input
);
}
void
loop(){
val = digitalRead(inputPin); //
read input value
if (val == HIGH) { //
check if the input is HIGH
digitalWrite(ledPin, HIGH); //
turn
if (pirState == LOW) { // we have just turned on
pirState = HIGH;
}
} else {
digitalWrite(ledPin, LOW); // turn LED OFF
if (pirState == HIGH){ // we have just turned off
pirState = LOW;
}
}
}
If
motion is detected the LED turns on. If no motion is detected, the LED remains
turned off.
Reed Sensor
The
Reed sensor module is also known as a magnetron module or reed switch. It is a
small electrical switch operated by an applied magnetic field, commonly used as
proximity sensor.
It
has a working voltage 3.3V-5V, with a digital output which is capable of
switching output (0 and 1).
This
module can be incorporated into program-controlled switches, copiers, washing
machines, refrigerators, cameras, electromagnetic relays, electronic weighing
scales, level meters, gas meters, water meters and so on.
Connect D0 (digital out) of
the Reed sensor module to pin 2 (PB3) of the ATTiny. Connect the VCC and GND to
VCC and GND of the ATTiny. Connect an LED to pin 6 (PB1) of the ATTiny and the
GND of the LED to GND of the ATTiny.
Once
the circuit is set up, construct a sketch similar to the one below ans upload
it to the ATTiny using the ISP.
int Reed = PB3;
int ledPin = PB1;
int val ;
void setup (){
pinMode(ledPin, OUTPUT);//
declare LED as output
pinMode (Reed, INPUT); //
declare Reed as input
}
void loop (){
val = digitalRead (Reed) ;
if (val == HIGH) {
digitalWrite(ledPin,
HIGH); // turn
else
{
digitalWrite(ledPin, LOW); // turn LED OFF
}
delay(2000);
}
When a magnet comes into close
proximity of the circuit, the sensor will switch and the LED will turn on. This
switching due to a magnetic field being present (or not) can be adapted and
incorporated into larger projects.
Hall
Effect Sensor
A Hall Effect sensor is a
transducer that varies its output voltage in response to a magnetic field. Hall
effect sensors are used for proximity switching, positioning, speed detection,
and current sensing applications.
In a Hall Effect sensor, a
thin strip of metal has a current applied along it. In the presence of a
magnetic field, the electrons in the metal strip are deflected toward one edge,
producing a voltage gradient across the short side of the strip (perpendicular
to the feed current). Hall effect sensors have an advantage over inductive
sensors in that, while inductive sensors respond to a changing magnetic field
which induces current in a coil of wire and produces voltage at its output,
Hall effect sensors can detect static (non-changing) magnetic fields.
Connect the VCC of
the sensor to the 5V pin of the ATTiny. Connect GND to GND and connect the DO
(digital out) of the sensor to pin2
(PB3) of the ATTiny. Connect an LED to pin pin 6 (PB1) of the ATTiny. Once the
circuit is set up, construct a sketch similar to the one below ans upload it to
the ATTiny using the ISP.
int Hall = PB3;
int ledPin =
PB1;
int val ;
void setup ()
{
pinMode(ledPin, OUTPUT);// declare LED as
output
pinMode (Hall, INPUT); // declare Reed as
input
}
void loop ()
{
val = digitalRead (Hall) ;
if (val == HIGH)
{
digitalWrite(ledPin, HIGH); // turn
}
else
{
digitalWrite(ledPin, LOW); // turn LED OFF ie magnetic field detected
}
delay(100);
}
As the magnet gets nearer
to the sensor (changing magnetic field), the digital value decreases and when
the value reached “LOW”, the LED will turn off.
Capacitive Touch
Sensor
The
TTP233B capacitive touch sensor can be used as an alternative to the tactile
push button switch in numerous applications. WIth the
normal state, the module output is low and it has a low power consumption; When
the sensor is touched, , the module
output state is high If it is not touched for 12 seconds,the switch state changes to low. i.e.to low-power
mode.The sensor requires a power supply of 2 ~ 5.5V DC.
The
example below turns an LED on for a tenth of a second when the sensor is
touched. Connect the VCC and GND of the module to VCC and GND (pins 8 and 4,
respectively) of the ATTiny. Connect the I/O pin to pin 2 (PB3) of the ATTiny.
Connect and LED to pin 6 (PB1) and ground. Upload the following sketch using
the Arduino ISP programmer.
int
sensor = PB3;
int
ledPin = PB1;
int
relay = 2;
boolean
currentState = LOW;
boolean
lastState = LOW;
boolean
RelayState = LOW;
void
setup() {
pinMode(ledPin, OUTPUT);
pinMode(sensor, INPUT);
}
void
loop() {
currentState = digitalRead(sensor);
if (currentState == HIGH && lastState
== LOW){
digitalWrite(ledPin, HIGH); // turn
delay(1);
if (sensor == HIGH){
digitalWrite(ledPin, LOW);
RelayState = LOW;
} else {
digitalWrite(ledPin, HIGH);
RelayState = HIGH;
delay(100);
digitalWrite(ledPin, LOW); //return LED to off
}
}
lastState = currentState;
}
Push Button
Switch
Push
button switches are easy to set up and can be used in innumerable situations.
When pressed, they connect two points in a circuit. This basic example turns on
an LED when the button is pressed. As well as breadboard, wires and the button
itself, you will need a 10k ohm resistor.
The
LED is attached to PB1 And the button attached to PB3. VCC and GND on the
ATTiny are connected to VCC and GND on the breadboard.
Upload
the following sketch using the Arduino ISP programmer.
.
const
int buttonPin = PB3; // the pushbutton
pin
const
int ledPin = PB1; // the LED pin
//
variables will change:
int
buttonState = 0; // variable for reading
the pushbutton status
void
setup() {
pinMode(ledPin,
OUTPUT); // initialize the LED pin
as an output
pinMode(buttonPin,
INPUT); // initialize the pushbutton
pin as an input
}
void
loop(){
buttonState
= digitalRead(buttonPin); // read the
state of the pushbutton
//if
pushbutton, the buttonState is HIGH:
if (buttonState == HIGH) {
digitalWrite(ledPin,
HIGH); // turn LED on
}
else
{ digitalWrite(ledPin, LOW); // turn LED off
}
}
This
basic circuit and program can serve as part of much more complex projects. Try
building a circuit that incorporates a push button (or more than one button)
with an LED to turn several LEDs on and/or off. You can use other components
and control them by push bouttons.
Stepper Motor with
ULN2003 Driver
Stepper
motors are used for position control. They divide a full rotation into a number
of equal “steps” so can be programmed to move to specific positions. They are
usually found in desktop printers, 3D printers, CNC to milling machines, and
anything else that requires precise positioning control.
ULN2003
IC is one of the most commonly used Motor driver integrated circuits (IC)
generally used when there is a need to drive high current loads using digital
logic circuits like Timers, Gates, microcontrollers etc.
This
example will show how to control a 28BYJ-48 stepper motor by using a ULN2003
driver board and ATTiny.
Usually,
the 28BYJ-48 stepper motor comes with a 5-pin connector that will fit to the
ULN2003 driver board .Connect the ULN2003 driver board to four digital pins on
the ATTiny as follows:
ATTiny ULN2003 Board
PB1 IN1
PB2 IN2
PB3 IN3
PB4 IN4
Upload
the following sketch using the Arduino ISP programmer.
uint8_t wire1 = PB1;
uint8_t wire2 = PB2;
uint8_t wire3 = PB3;
uint8_t wire4 = PB4;
const uint16_t _delay = 50; /* delay in between two
steps. minimum delay more the rotational speed */
void sequence(bool a, bool b, bool c, bool d){ /* four step sequence to stepper motor */
digitalWrite(wire1,
a);
digitalWrite(wire2, b);
digitalWrite(wire3, c);
digitalWrite(wire4, d);
delay(_delay);
}
void setup() {
pinMode(wire1,
OUTPUT); /* set four wires as output */
pinMode(wire2,
OUTPUT);
pinMode(wire3,
OUTPUT);
pinMode(wire4,
OUTPUT);
}
void loop() {
/* Rotation in
one direction */
for(int i = 0;
i<12; i++)
{
sequence(HIGH, LOW, LOW, LOW);
sequence(HIGH, HIGH, LOW, LOW);
sequence(LOW, HIGH, LOW, LOW);
sequence(LOW, HIGH, HIGH, LOW);
sequence(LOW, LOW, HIGH, LOW);
sequence(LOW, LOW, HIGH, HIGH);
sequence(LOW, LOW, LOW, HIGH);
sequence(HIGH, LOW, LOW, HIGH);
}
sequence(HIGH,
LOW, LOW, LOW);
/* Rotation in
opposite direction */
for(int j = 0;
j<12; j++)
{
sequence(LOW, LOW, LOW, HIGH);
sequence(LOW, LOW, HIGH, HIGH);
sequence(LOW, LOW, HIGH, LOW);
sequence(LOW, HIGH, HIGH, LOW);
sequence(LOW, HIGH, LOW, LOW);
sequence(HIGH, HIGH, LOW, LOW);
sequence(HIGH, LOW, LOW, LOW);
sequence(HIGH, LOW, LOW, HIGH);
}
sequence(LOW,
LOW, LOW, HIGH);
}
Once the program is uploaded and running you will see
the stepper motor moving to different positions ansd in different directions.
You can use this as a basis for other projects by altering the program to
enable the stepper motor to perform specific movements.
Shift Register 74HC595N
A shift register is an integrated circuit that converts serial
information to parallel. It requires 3 pins (as well as power and ground) from
a microcontroller but allows us to have a total of 8 outputs. Earlier in this
Section we have seen that we used one pin on the ATTiny to control one LED but
by using a shift register, we can control 8 LEDs using only pins 3 pins.
The 74HC595
is a shift register which works on Serial IN Parallel OUT protocol. It receives
data serially from the microcontroller and then sends out this data through
parallel pins. The output pins can be increased by 8 using the single chip.
Earlier in this Section we used one pin on
the ATTiny to control one LED but by using a shift register, we can control 8
LEDs using just 3 pins.. For illustration purposes, the examples below will use pins 5, 3 and 6
on the ATTiny to control the shift register. Pin PB0 is connected to the serial
data pin, pin PB4 is connected to the clock pin and pin PB1 is connected to the
latch pin.
The 8 LEDs are connected with their shorter legs
(cathodes) to GND, whilst their longer legs (anodes) are connected to the shift
register output pins (pins 1-7 and 15) via 220 ohm resistors. Pins 5, 6 and 3 (PB0, PB1 and
PB4) of the ATTiny are connected to pins 14, 12 and 11 respectively of the 74HC595 shift register.
Counting
The
first example counts from 0 to 255 and displays the number on the LEDs.
// Adapted from https://www.arduino.cc/en/tutorial/ShiftOut
int
latchPin = PB1; //Pin connected to ST_CP of 74HC595
int
clockPin = PB4; //Pin connected to SH_CP
of 74HC595
int
dataPin = PB0; //Pin connected to DS of 74HC595
void
setup() {
//set pins to output so you can control the
shift register
pinMode(latchPin, OUTPUT);
pinMode(clockPin, OUTPUT);
pinMode(dataPin, OUTPUT);
}
void
loop() {
// count from 0 to 255 and display the number
// on the LEDs
for (int numberToDisplay = 0; numberToDisplay
< 256; numberToDisplay++) {
// take the latchPin low so
// the LEDs don't change while you're
sending in bits:
digitalWrite(latchPin, LOW);
// shift out the bits:
shiftOut(dataPin, clockPin, MSBFIRST,
numberToDisplay);
//take the latch pin high so the LEDs will
light up:
digitalWrite(latchPin, HIGH);
// pause before next value:
delay(500);
}
}
Upload to the ATTiny using the programmer. You will
see the LEDs turning on and off as the count goes from 0 to 256.
Using An Array
The second example uses an array to turn the LEDs on
and off. The connections are as in the example above. Upload the following to
the ATTiny using the programmer.
/*
Shift Register Example
Turning on the outputs of a 74HC595 using an
array
Hardware:
* 74HC595 shift register
* LEDs attached to each of the outputs of the
shift register
*/
//Pin connected
to ST_CP of 74HC595
int latchPin =
PB1;
//Pin connected
to SH_CP of 74HC595
int clockPin =
PB4;
////Pin
connected to DS of 74HC595
int dataPin =
PB0;
//holders for
infromation you're going to pass to shifting function
byte data;
byte
dataArray[10];
void setup() {
//set pins to output because they are
addressed in the main loop
pinMode(latchPin, OUTPUT);
Serial.begin(9600);
//Binary notation as comment
dataArray[0] = 0xFF; //0b11111111
dataArray[1] = 0xFE; //0b11111110
dataArray[2] = 0xFC; //0b11111100
dataArray[3] = 0xF8; //0b11111000
dataArray[4] = 0xF0; //0b11110000
dataArray[5] = 0xE0; //0b11100000
dataArray[6] = 0xC0; //0b11000000
dataArray[7] = 0x80; //0b10000000
dataArray[8] = 0x00; //0b00000000
dataArray[9] = 0xE0; //0b11100000
//function that blinks all the LEDs
//gets passed the number of blinks and the
pause time
blinkAll_2Bytes(2,500);
}
void loop() {
for (int j = 0; j < 10; j++) {
//load the light sequence you want from
array
data = dataArray[j];
//ground latchPin and hold low for as long
as you are transmitting
digitalWrite(latchPin, 0);
//move 'em out
shiftOut(dataPin, clockPin, data);
//return the latch pin high to signal chip
that it
//no longer needs to listen for information
digitalWrite(latchPin, 1);
delay(300);
}
}
// the heart of
the program
void
shiftOut(int myDataPin, int myClockPin, byte myDataOut) {
// This shifts 8 bits out MSB first,
//on the rising edge of the clock,
//clock idles low
//internal function setup
int i=0;
int pinState;
pinMode(myClockPin, OUTPUT);
pinMode(myDataPin, OUTPUT);
//clear everything out just in case to
//prepare shift register for bit shifting
digitalWrite(myDataPin, 0);
digitalWrite(myClockPin, 0);
//for each bit in the byte myDataOut
//NOTICE THAT WE ARE COUNTING DOWN in our for
loop
//This means that %00000001 or "1"
will go through such
//that it will be pin Q0 that lights.
for (i=7; i>=0; i--) {
digitalWrite(myClockPin, 0);
//if the value passed to myDataOut and a
bitmask result
// true then... so if we are at i=6 and our
value is
// %11010100 it would the code compares it
to %01000000
// and proceeds to set pinState to 1.
if ( myDataOut & (1<<i) ) {
pinState= 1;
}
else {
pinState= 0;
}
//Sets the pin to HIGH or LOW depending on
pinState
digitalWrite(myDataPin, pinState);
//register shifts bits on upstroke of clock
pin
digitalWrite(myClockPin, 1);
//zero the data pin after shift to prevent
bleed through
digitalWrite(myDataPin, 0);
}
//stop shifting
digitalWrite(myClockPin, 0);
}
//blinks the whole register based on the number of
times you want to
//blink "n" and the pause between them
"d"
//starts with a moment of darkness to make sure the
first blink
//has its full visual effect.
void blinkAll_2Bytes(int n, int d) {
digitalWrite(latchPin, 0);
shiftOut(dataPin,
clockPin, 0);
shiftOut(dataPin, clockPin, 0);
digitalWrite(latchPin, 1);
delay(200);
for (int x =
0; x < n; x++) {
digitalWrite(latchPin, 0);
shiftOut(dataPin, clockPin, 255);
shiftOut(dataPin, clockPin, 255);
digitalWrite(latchPin, 1);
delay(d);
digitalWrite(latchPin, 0);
shiftOut(dataPin, clockPin, 0);
shiftOut(dataPin, clockPin, 0);
digitalWrite(latchPin, 1);
delay(d);
}
}
Linking Shift Registers
To get even more outputs from the ATTiny, shift registers
can be linked together.A second shift register can be connected to the first by
connecting pin 9 (serial out)of the first to pin 14 (serial data input) of the
second shift register. LEDs can be connected to the second shift register as
illustrated in the previous examples.
Upload the following to the ATTiny using the
programmer.
//Pin connected to ST_CP of
74HC595
int latchPin = PB1;
//Pin connected to SH_CP of
74HC595
int clockPin = PB4;
////Pin connected to DS of
74HC595
int dataPin = PB0;
void setup() {
//Start Serial for debuging purposes
Serial.begin(9600);
//set pins to output because they are
addressed in the main loop
pinMode(latchPin, OUTPUT);
}
void loop() {
//count up routine
for (int j = 0; j < 256; j++) {
//ground latchPin and hold low for as long
as you are transmitting
digitalWrite(latchPin, 0);
//count up on GREEN LEDs
shiftOut(dataPin, clockPin, j);
//count down on RED LEDs
shiftOut(dataPin, clockPin, 255-j);
//return the latch pin high to signal chip
that it
//no longer needs to listen for information
digitalWrite(latchPin, 1);
delay(1000);
}
}
void shiftOut(int myDataPin,
int myClockPin, byte myDataOut) {
// This shifts 8 bits out MSB first,
//on the rising edge of the clock,
//clock idles low
//internal function setup
int i=0;
int pinState;
pinMode(myClockPin, OUTPUT);
pinMode(myDataPin, OUTPUT);
//clear everything out just in case to
//prepare shift register for bit shifting
digitalWrite(myDataPin, 0);
digitalWrite(myClockPin, 0);
//for each bit in the byte myDataOut
//NOTICE THAT WE ARE COUNTING DOWN in our for
loop
//This means that %00000001 or "1"
will go through such
//that it will be pin Q0 that lights.
for (i=7; i>=0; i--) {
digitalWrite(myClockPin, 0);
//if the value passed to myDataOut and a
bitmask result
// true then... so if we are at i=6 and our
value is
// %11010100 it would the code compares it
to %01000000
// and proceeds to set pinState to 1.
if ( myDataOut & (1<<i) ) {
pinState= 1;
}
else {
pinState= 0;
}
//Sets the pin to HIGH or LOW depending on
pinState
digitalWrite(myDataPin, pinState);
//register shifts bits on upstroke of clock
pin
digitalWrite(myClockPin, 1);
//zero the data pin after shift to prevent
bleed through
digitalWrite(myDataPin, 0);
}
//stop shifting
digitalWrite(myClockPin, 0);
}
Numerous shift registers can
be linked in this was to give the ATTiny the ability to control numerous
outputs whilst using only three pins.
Line Decoder SN74HCT138N
The 74HCT138 is a decoder/demultiplexer
(DEMUX) inverting. That means that it can turn 3 digital outputs into 8 digital
outputs. Inverting means that the encoded output will actually be LOW, not HIGH
as would be expected. The 74HC138 / 74HCT138 decoders accept three binary
weighted address inputs (A0, A1, A2) and when enabled, provide 8 mutually
exclusive active LOW outputs (Y0 to Y7).
Truth table
74HC138N ATTiny
A0 PB1
A1 PB2
A2 PB3
E3 PB4
E1 GND
E2 GND
VCC 5V
GND GND
Y0-Y4 LED1 - 5
E1
and E2 are LOW (GND), therefore, E3 (connected to ATTiny pin PB4) is HIGH.
Upload the following to the ATTiny using the
programmer. The program will cycle through the 5 LEDs.that are connected to the
line decoder.
const
int selA0 = PB1;
const
int selA1 = PB2;
const
int selA2 = PB3;
const
int E3 = PB4;
void
setup()
{
//
initialize the control outputs
pinMode(selA0,
OUTPUT);
pinMode(selA1,
OUTPUT);
pinMode(selA2,
OUTPUT);
pinMode(E3,
OUTPUT);
digitalWrite(selA0,
LOW);
digitalWrite(selA1,
LOW);
digitalWrite(selA2,
LOW);
digitalWrite(E3,
HIGH);
}
void
loop()
{
/*turn
on LED 1 */
digitalWrite(selA0,
LOW);
digitalWrite(selA1,
LOW);
digitalWrite(selA2,
LOW);
delay(1000);
/*turn
on LED 2 */
digitalWrite(selA0,
HIGH);
digitalWrite(selA1,
LOW);
digitalWrite(selA2,
LOW);
delay(1000);
/*turn
on LED 3 */
digitalWrite(selA0,
LOW);
digitalWrite(selA1,
HIGH);
digitalWrite(selA2,
LOW);
delay(1000);
/*turn
on LED 4 */
digitalWrite(selA0,
HIGH);
digitalWrite(selA1,
HIGH);
digitalWrite(selA2,
LOW);
delay(1000);
/*turn
on LED 5 */
digitalWrite(selA0,
LOW);
digitalWrite(selA1,
LOW);
digitalWrite(selA2,
HIGH);
delay(1000);
}
The core that is used from here onwards was devised and developed by James Sleeman. Initially, I began experimenting with it just for serial communication on the ATTiny but it can be used extensively for numerous applicaions. The diagram below shows the pins that can be used for various functions and these will be referenced in the examples that follow.
Important:
pinMode() must only be used with the "digital pin
numbers" 0 .. n
pins default to INPUT, you do not need to pinMode() to
INPUT if you are only ever doing an analogRead() from the pin.
analogRead() must only be used with the "analog
pin numbers" A0 .. n
The Baud Rate is IGNORED on the Tiny13 due to using a
simplified serial. The actual Baud Rate used is dependant on the processor
speed.
9.6MHz will be 57600 Baud
4.8MHz will be 9600 Baud
1.2MHz will be 9600 Baud
The default settings in the Arduino IDE should be set
to the following:
Tools >
Board :
ATTiny13
Tools >
Processor Version : ATTiny13
Tools > Use
Bootloader : No (ISP Programmer Upload)
Tools >
Processor Speed :
9.6MHz Internal Oscillator
Tools >
Millis, Tone Suppor : Millis Available, No Tone
Tools >
Millis Accuracy :
1.666%
Tools >
Print Support :
Bin, Hex, Dec Supported
Tools >
Serial Support :
Half Duplex, Read+Write
1. Copy this URL
https://raw.githubusercontent.com/sleemanj/optiboot/master/dists/package_gogo_diy_attiny_index.json
2.
Open the Arduino IDE and chooe File > Preferences locate the "Additional
Boards Manager URLs" at the bottom of the window
3.
Click the window icon next to that field to edit it
4.
Paste the URL from 1 into this field on a new line and click OK, and OK to
close the preferences
5.
Go to Tools > Board > Boards Manager
6.
Wait for the update download to finish
7.
In "Filter your search" type Tiny13 and choose to install the
"DIY ATtiny by James Sleeman"
8.
Close the boards manager
9.
Choose Tools > Board > DIY ATtiny > ATtiny13
10.
Choose Tools > Burn Bootloader to set the fuses correctly if you are not
certain they are correct
Serial
Communication
Serial
communication on the ATTiny13A is quite straight forward once the IDE and board
are set up correctly. To ensure that the board is set correctly, the following
steps should be taken with the ATTiny13A board connected to the Arduino ISP as
described earlier: Note that all examples following this topic use this set up
(core).
To
test that the board is set up correctly (see previously) and to demonstrate
serial output, choose File > Examples
> ATTinyCore > Tiny13 > Communication > ASCII Table. Upload sketch
using the Arduino ISP programmer.
Disconnect
programmer and then connect the “serial adapter” i.e. pin 5 of the ATTiny
connects to TX (pin 1) of the Arduino, and pin 6 of the ATTiny connects to RX (pin 0) of the
Arduino
Open
the Serial Monitor and select 57600 baud rate. The program will print a table
of ASCII characters to the Serial Monitor
A
very useful and important part of this program is the confirmation that the
usual parameters for printing to the Serial Monitor can be used (to a certain
extent). Below is a very basic program which initializes the baud rate, prints “Hello”, waits for a second (1000
milliseconds) and then repeats indefinitely.
void
setup() {
// put
your setup code here, to run once:
Serial.begin(57600);
}
void
loop() {
// put your main code here, to run
repeatedly:
Serial.println("Hello");
delay(1000);
}
Now
we have the ability to print to the Sereial Monitor, we can incorporate this
feature into numerous applications and projects.
Serial
Menu
The following example prints a menu to the
Serial Monitor. There are four menu options to choose from. The user can select
an option by sending a single character that the program reads. Depending on
the option chosen, the program will write a response to the Serial Monitor.
In the Arduino IDE, select File >
Examples > ATTinyCore > Tiny13 > Communication > MenuDemo. Upload
sketch using the Arduino ISP programmer.
void setup()
{
Serial.begin(57600); // Set the baud rate
}
void loop()
{
Serial.println(F("\n~~ Tiny Menu ~~\n"));
Serial.println(F("1. Say Hello"));
Serial.println(F("2. Say Goodbye"));
Serial.println(F("3. Sing"));
Serial.println(F("4. Jack"));
switch(Serial.read_char_blocking())
{
case '1':
Serial.println(F(">> Hello"));
break;
case '2':
Serial.println(F(">> Goodbye"));
break;
case '3':
Serial.println(F(">> Daisy, daisy, give me your answer
do."));
break;
case '4':
for(uint8_t x = 0; x < 100; x++)
{
Serial.println(F("All work and no play makes Jack a dull
boy."));
}
break;
default:
Serial.println(F("* Unkown Command *"));
break;
}
delay(1000);
}
Disconnect programmer and then connect the
“serial adapter” i.e. pin 5 of the ATTiny connects to TX (pin 1) of the
Arduino, and pin 6 of the ATTiny
connects to RX (pin 0) of the Arduino
Open the Serial Monitor and select 57600 baud rate if not already selected.
By sending an
option (1,2,3 or 4), the ATTiny will respond according to the selection. This
example demonstrates how two way communication with the ATTiny can be
implemented.
Read Serial ASCII
String
The following example reads a string from
millis() will "lose time" for the
period you are reading the string.
In the Arduino IDE, select File >
Examples > ATTinyCore > Tiny13 > Communication > ReadASCIIString. Upload sketch using
the Arduino ISP programmer.
void setup()
{
Serial.begin(57600); // Set the baud
rate }
void loop()
{
Serial.println(F("What is your name
traveller?"));
char buf[10];
do
{
Serial.read_str(buf, sizeof(buf));
} while(!buf[0]);
Serial.print(F("Nice to meet you
"));
Serial.println(buf);
}
Disconnect programmer and then connect the
“serial adapter” i.e. pin 5 of the ATTiny connects to TX (pin 1) of the
Arduino, and pin 6 of the ATTiny
connects to RX (pin 0) of the Arduino
Open the Serial Monitor and select 57600
baud rate if not already selected.
You will be promted to input your name.
Once you have done this, the program will serially print “Nice to meet you
(your name)”.
Serial Digital Clock
choose
File > Examples > ATTinyCore > Tiny13 > Communication >
SerialDigitalClock. Upload sketch using the Arduino ISP programmer.
uint8_t hours = 0, mins = 0, secs
= 0;
uint32_t lastMillis = 0;
void setup()
{
// If you have a calibration
number (OSCCAL) for your chip enter it here
// and uncomment. The OSCCAL is specific to the chip you are
looking at
// right now, not the type of
chip, the actual physical chip you have in
// your hand, at the specific
temperature in your vicinity right now,
// whatever that is. If you have not calibrated it, then the chip
will
// have a default calibration
value built into it at the Atmel factor.
// OSCCAL=84;
Serial.begin(57600); // NOTICE
the baud rate specified is ignored on the T13
//
Instead it is hard coded as follows...
// Processors at 9.6MHz ==> 57600
// Processors at 4.8 and 1.2MHz ==> 9600
}
void loop()
{
if(millis()-lastMillis >=
REAL_MILLIS(1000))
{
lastMillis += REAL_MILLIS(1000);
secs++;
if(secs == 60)
{
secs = 0;
mins++;
}
if(mins == 60)
{
mins = 0;
hours++;
}
if(hours == 24)
{
hours = 0;
}
Serial.print( hours );
Serial.print(':');
Serial.print( mins );
Serial.print(':');
Serial.println(secs);
}
}
Using
the techniques above, you could expect to gain or lose 5 to 10 minutes a day.
If you have not calibrated the ATTiny oscillator then the error will
potentially be quite a lot more.. This can be done by using the OSCCAL program
in the Examples folder.
Note
that even though the example is a "clock" we don't need totally
accurate millis because we are using REAL_MILLIS().
As
long as millis() ticks along at a constant rate, REAL_MILLIS() provides an
adjustment to be more accurate.
However
there is still interaction between REAL_MILLIS() and millis' accuracy.
Disconnect
programmer and then connect the “serial adapter” i.e. pin 5 of the ATTiny
connects to TX (pin 1) of the Arduino, and pin 6 of the ATTiny connects to RX (pin 0) of the
Arduino
Open
the Serial Monitor and select 57600 baud rate. The program will a running
digital clock to the Serial Monitor.
Random Number Generator
This program
will generate random numbers and print them to the Serial Monitor. The ATTiny13A is too small to use the stdlb
random number generator, so here is a simplistic 16bit XOR random number
generator. The random numbers are not particularly great in a statistical
sense, but the program does produce some randomness.
The
tiny_random() function will return numbers from 0 to 65534 inclusive. The
implementation is from adapted frpm here:
http://www.arklyffe.com/main/2010/08/29/xorshift-pseudorandom-number-generator/
(modified to subtract 1 from the result to get
it rooted to zero). It's implemented in
the WMath.cpp in this core.
Upload the following sketch via the
Arduino ISP programmer (the program is available in the Examples folder).
void setup()
{
Serial.begin(57600);
//
Seed the generator
tiny_srandom(analogRead(A3));
}
void loop()
{
Serial.println(tiny_random());
delay(1000);
}
Disconnect the ISP programmer and connect
the serial adapter and open the Serial Monitor.
Physical Pixel
This is an example of using the ATTiny to
receive data from the computer. The ATTiny turns on an LED when it receives the
character 'H', and turns off the LED when it receives the character 'L'. The
data is sent from the Arduino Serial Monitor.
Upload the following sketch via the
Arduino ISP programmer.
const int ledPin = PB4; // physical pin 3
//int incomingByte; // a variable to read incoming serial
data into
void setup() {
Serial.begin(57600);
pinMode(ledPin, OUTPUT);
}
void loop() {
Serial.println ("Choose L or
H");
char buf[10];
do
{
Serial.read_str(buf, sizeof(buf));
}
while(!buf[0]);
if ((buf) == 'H') {
digitalWrite(ledPin, HIGH);
}
// if it's an L (ASCII 76) turn off the LED:
if ((buf) == 'L') {
digitalWrite(ledPin, LOW);
}
delay(2000);
//
Serial.println(buf);
}
Disconnect the ISP programmer and connect
the serial adapter and connect an LED to pin 3 (PB4) of te ATTiny (longest leg
of LED to this pin, shortest leg of LED to GND (pin 4). Open the Serial
Monitor.
When you send “H” via the Serial Monitor,
the LED will turn on. Conversley, when you send “L” via the Serial Monitor, the
LED will turn off.
Randomly flashing
LED with PRNG
Here an LED is
made to randomly flash using BBS (Blum-Blum-Shumb) algorithm as PRNG (Pseudo
Random Number Generator) to generate 16-bit (pseudo) random numbers. The less significant
bit of randomly generated number is a major factor in decision to turn the the
LED on/off.
Attach an LED via a 220 ohm resistor to
pin 5 (PB0) of the ATTiny. Connect the other leg (shorter leg) of the LED to
GND. Upload the following sketch using the Arduino ISP programmer.
#include <avr/io.h>
#include <util/delay.h>
#define LED_PIN PB0
#define BBS_P (13)
#define BBS_Q (97)
#define BBS_SEED (123)
#define DELAY (64)
static uint16_t m;
static uint16_t x;
static uint16_t r;
/* Initialize vector of BBS algorithm, where:
-> p: prime number
-> q: prime number
-> seed: random integer greater then 1,
which is co-prime to 'm',
where 'm = p*q' and
'GCD(m, seed) == 1'
Note that value of 'm' must be less than '(2^16) - 1'
*/
static void
bbs_init(uint16_t p, uint16_t q, uint16_t seed)
{
m = p * q;
r = x = seed;
}
static uint16_t
bbs_next(void)
{
return (r = (r * x) % m);
}
int
main(void)
{
/* setup */
DDRB |= _BV(LED_PIN); // set LED
pin as OUTPUT
bbs_init(BBS_P, BBS_Q, BBS_SEED);
// initialize BBS alg.
/* loop */
while (1) {
if (bbs_next() & 1) {
PORTB |= _BV(LED_PIN);
} else {
PORTB &= ~_BV(LED_PIN);
}
_delay_ms(DELAY);
}
Here, we are going to use an LM35 precision analog temperature
sensor. It has 3-pins; 5V, GND and temperature.
As you look at the flat face of the sensor with the pins hanging
downwards, the left pin is the 5V pin and should be attached to the 5V pin (pin
8) of the ATTiny. The right pin of the sensor is ground and should be connected
to the ATTiny GND pin (pin 4). Finally, the central pin should be connected
directly to pin 3 (PB4) of the ATTiny. Notice that we are not using a
breadboard here. This is because if we were to use a breadboard, the
temperature wouldn’t be very stable. If you must use a breadboard, you would be
well advised to add some sort of filter.
Upload the following sketch via the Arduino ISP programmer.
Disconnect the ISP programmer and connect the serial adapter and open the
Serial Monitor. You should see the temperature reading value being updated
every two seconds. If you grab hold of the sensor or breathe on it, you should
see the temperature rise. Try altering the delay so that you see the
temperature being updated at different intervals (remember that this is
milliseconds so delay(2000); pauses for two seconds).
const uint8_t INPUT_PIN = A2; //pin 3
void setup()
{
Serial.begin(57600);
}
void loop()
{
uint16_t sensorValue =
analogRead(INPUT_PIN);
Serial.println
(sensorValue)*1000/1024*5000;
delay (2000);
}
When the ATTiny is using the 5V reference with 10bit analog to
digital conversion (ADC) it has a 1024 count. Therefore, we have to perform the
following calculation on the sensor measurement to give true temperature in
Centigrade.
Temprature = (sensorValue)*1000/1024*5000, i.e. temp = Vin * 1000
/ 1024 * Vref [milivolts]
The serial output can be enhanced by inserting the word “Temperature”
before and the words “degrees centigrade” after the temperature.The temperature
sensorcan easily be incorporated into larger projects such as a weather station
project.
TM1637 Seven Segment Display
Modules based on TM1637 provide two signal connections (CLK and DIO) and
two power connections (VCC and GND). Signal pins can be connected to any pair
of digital pins of the AVR chip. Signal pins configuration is defined at the
top of library header file, where it can be modifed.
The example below will enable the ATTiny to read the temperature from a
LM35 sensor (see earlier) and display it on the TM1635 module.
Upload the following sketch via the
Arduino ISP programmer. Though the
sketch is quite long, it has been included in its entireity so that the reader
can see the construction and the necessary level of programming required. This
can be much reduced by using a library.
#include <stdint.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <util/delay.h>
#define LM35_DATA_PIN PB4
#define TM1637_DIO_HIGH() (PORTB
|= _BV(TM1637_DIO_PIN))
#define TM1637_DIO_LOW() (PORTB
&= ~_BV(TM1637_DIO_PIN))
#define TM1637_DIO_OUTPUT() (DDRB
|= _BV(TM1637_DIO_PIN))
#define TM1637_DIO_INPUT() (DDRB
&= ~_BV(TM1637_DIO_PIN))
#define TM1637_DIO_READ()
(((PINB & _BV(TM1637_DIO_PIN)) > 0) ? 1 : 0)
#define TM1637_CLK_HIGH() (PORTB
|= _BV(TM1637_CLK_PIN))
#define TM1637_CLK_LOW() (PORTB
&= ~_BV(TM1637_CLK_PIN))
// Main Settings
#define TM1637_DIO_PIN PB0
#define TM1637_CLK_PIN PB1
#define TM1637_DELAY_US (5)
#define TM1637_BRIGHTNESS_MAX (7)
#define TM1637_POSITION_MAX (4)
// TM1637 commands
#define TM1637_CMD_SET_DATA 0x40
#define TM1637_CMD_SET_ADDR 0xC0
#define TM1637_CMD_SET_DSIPLAY
0x80
// TM1637 data settings (use bitwise OR to contruct complete command)
#define TM1637_SET_DATA_WRITE
0x00 // write data to the display register
#define TM1637_SET_DATA_READ
0x02 // read the key scan data
#define TM1637_SET_DATA_A_ADDR
0x00 // automatic address increment
#define TM1637_SET_DATA_F_ADDR
0x04 // fixed address
#define TM1637_SET_DATA_M_NORM
0x00 // normal mode
#define TM1637_SET_DATA_M_TEST
0x10 // test mode
// TM1637 display control command set (use bitwise OR to consruct
complete command)
#define TM1637_SET_DISPLAY_OFF
0x00 // off
#define TM1637_SET_DISPLAY_ON
0x08 // on
static void TM1637_send_config(const uint8_t enable, const uint8_t
brightness);
static void TM1637_send_command(const uint8_t value);
static void TM1637_start(void);
static void TM1637_stop(void);
static uint8_t TM1637_write_byte(uint8_t value);
static uint8_t _config = TM1637_SET_DISPLAY_ON | TM1637_BRIGHTNESS_MAX;
static uint8_t _segments = 0xff;
static void LM35_init(void);
static int LM35_read(void);
static int analog_read(uint8_t pin);
/**
* Initialize TM1637 display
driver.
* Clock pin (TM1637_CLK_PIN) and
data pin (TM1637_DIO_PIN)
* are defined at the top of this
file.
*/
void TM1637_init(const uint8_t enable, const uint8_t brightness);
/**
* Turn display on/off.
* value: 1 - on, 0 - off
*/
void TM1637_enable(const uint8_t value);
/**
* Set display brightness.
* Min value: 0
* Max value: 7
*/
void TM1637_set_brightness(const uint8_t value);
/**
* Display raw segments at position
(0x00..0x03)
*
* bits:
* -- 0 --
* |
|
* 5
1
* |
|
* -- 6 --
* |
|
* 4
2
* |
|
* -- 3 --
*
* Example segment configurations:
* - for character 'H',
segments=0b01110110
* - for character '-', segments=0b01000000
* - etc.
*/
void TM1637_display_segments(const uint8_t position, const uint8_t
segments);
/**
* Display digit ('0'..'9') at
position (0x00..0x03)
*/
void TM1637_display_digit(const uint8_t position, const uint8_t digit);
/**
* Display colon on/off.
* value: 1 - on, 0 - off
*/
void TM1637_display_colon(const uint8_t value);
/**
* Clear all segments (including
colon).
*/
void TM1637_clear(void);
PROGMEM const uint8_t _digit2segments[] =
{
0x3F, // 0
0x06, // 1
0x5B, // 2
0x4F, // 3
0x66, // 4
0x6D, // 5
0x7D, // 6
0x07, // 7
0x7F, // 8
0x6F // 9
};
int main(void)
{
int temp;
uint8_t firstdig;
uint8_t seconddig;
uint8_t thirddig;
/* setup */
TM1637_init(1/*enable*/,
5/*brightness*/);
/* setup */
LM35_init();
/* loop */
while (1) {
// read ADC value 16 times
and compute mean for accuracy
temp = 0;
for(uint8_t counts = 0;
counts < 16; counts++) temp = temp +
LM35_read();
temp = temp >> 4;
// wyswietlenie znaku stopni NA OSTATNIEJ
POZYCJI
TM1637_display_segments(3,
99);
//przeliczenie cyfr na
wszystkich pozycjach
firstdig = temp /
100;
seconddig = (temp -
(firstdig * 100))/ 10;
thirddig =
temp - (firstdig * 100) - (seconddig*10);
// wyswietlamy od pozycji
1giej na wyswietlaczu
// ostatnia pozycja to znak
minus lub stopnie
TM1637_display_digit(0, firstdig);
TM1637_display_digit(1, seconddig);
TM1637_display_digit(2, thirddig);
// pomiar co pol sekundy
_delay_ms(500);
}
/* loop */
}
void
LM35_init(void)
{
DDRB &= ~_BV(LM35_DATA_PIN);
// set data pin as INPUT
}
int
LM35_read(void)
{
int temp;
temp =
analog_read(LM35_DATA_PIN); // read analog value from sensor
temp = ((((uint32_t)temp *
1000UL) >> 10) * 5); // convert value using euqation temp = Vin * 1000 /
1024 * Vref [milivolts]
return temp;
}
int
analog_read(uint8_t pin)
{
uint8_t low, high;
switch(pin) {
case PB2: ADMUX = _BV(MUX0);
break; // ADC1
case PB4: ADMUX =
_BV(MUX1); break; // ADC2
case PB3: ADMUX =
_BV(MUX0)|_BV(MUX1); break; // ADC3
case PB5: // ADC0
default: ADMUX = 0; break;
}
ADMUX &= ~_BV(REFS0); //
explicit set VCC as reference voltage (5V)
ADCSRA |= _BV(
ADCSRA |= _BV(ADSC); // Run single conversion
while(bit_is_set(ADCSRA, ADSC));
// Wait conversion is done
// Read values
low = ADCL;
high = ADCH;
// combine two bytes
return (high << 8) |
low;
}
void
TM1637_init(const uint8_t enable, const uint8_t brightness)
{
DDRB |=
(_BV(TM1637_DIO_PIN)|_BV(TM1637_CLK_PIN));
PORTB &=
~(_BV(TM1637_DIO_PIN)|_BV(TM1637_CLK_PIN));
TM1637_send_config(enable,
brightness);
}
void
TM1637_enable(const uint8_t value)
{
TM1637_send_config(value, _config
& TM1637_BRIGHTNESS_MAX);
}
void
TM1637_set_brightness(const uint8_t value)
{
TM1637_send_config(_config &
TM1637_SET_DISPLAY_ON,
value &
TM1637_BRIGHTNESS_MAX);
}
void
TM1637_display_segments(const uint8_t position, const uint8_t segments)
{
TM1637_send_command(TM1637_CMD_SET_DATA | TM1637_SET_DATA_F_ADDR);
TM1637_start();
TM1637_write_byte(TM1637_CMD_SET_ADDR | (position &
(TM1637_POSITION_MAX - 1)));
TM1637_write_byte(segments);
TM1637_stop();
}
void
TM1637_display_digit(const uint8_t position, const uint8_t digit)
{
uint8_t segments = (digit < 10
? pgm_read_byte_near((uint8_t *)&_digit2segments + digit) : 0x00);
if (position == 0x01) {
segments = segments |
(_segments & 0x80);
_segments = segments;
}
TM1637_display_segments(position,
segments);
}
void
TM1637_display_colon(const uint8_t value)
{
if (value) {
_segments |= 0x80;
} else {
_segments &= ~0x80;
}
TM1637_display_segments(0x01,
_segments);
}
void
TM1637_clear(void)
{
uint8_t i;
for (i = 0; i <
TM1637_POSITION_MAX; ++i) {
TM1637_display_segments(i,
0x00);
}
}
void
TM1637_send_config(const uint8_t enable, const uint8_t brightness)
{
_config = (enable ?
TM1637_SET_DISPLAY_ON : TM1637_SET_DISPLAY_OFF) |
(brightness >
TM1637_BRIGHTNESS_MAX ? TM1637_BRIGHTNESS_MAX : brightness);
TM1637_send_command(TM1637_CMD_SET_DSIPLAY | _config);
}
void
TM1637_send_command(const uint8_t value)
{
TM1637_start();
TM1637_write_byte(value);
TM1637_stop();
}
void
TM1637_start(void)
{
TM1637_DIO_HIGH();
TM1637_CLK_HIGH();
_delay_us(TM1637_DELAY_US);
TM1637_DIO_LOW();
}
void
TM1637_stop(void)
{
TM1637_CLK_LOW();
_delay_us(TM1637_DELAY_US);
TM1637_DIO_LOW();
_delay_us(TM1637_DELAY_US);
TM1637_CLK_HIGH();
_delay_us(TM1637_DELAY_US);
TM1637_DIO_HIGH();
}
uint8_t
TM1637_write_byte(uint8_t value)
{
uint8_t i, ack;
for (i = 0; i < 8; ++i, value
>>= 1) {
TM1637_CLK_LOW();
_delay_us(TM1637_DELAY_US);
if (value & 0x01) {
TM1637_DIO_HIGH();
} else {
TM1637_DIO_LOW();
}
TM1637_CLK_HIGH();
_delay_us(TM1637_DELAY_US);
}
TM1637_CLK_LOW();
TM1637_DIO_INPUT();
TM1637_DIO_HIGH();
_delay_us(TM1637_DELAY_US);
ack = TM1637_DIO_READ();
if (ack) {
TM1637_DIO_OUTPUT();
TM1637_DIO_LOW();
}
_delay_us(TM1637_DELAY_US);
TM1637_CLK_HIGH();
_delay_us(TM1637_DELAY_US);
TM1637_CLK_LOW();
_delay_us(TM1637_DELAY_US);
TM1637_DIO_OUTPUT();
return ack;
}
The temperature from a LM35 sensor will be displayed on the TM1635
module.
TM1637 Seven Segment Display With
Library.
A dedicated TM1637 Seven Segment Display
library has been written specifically for the ATTiny13. This enables the user
to be more flexible with the programming of the microcontroller and simplifies
the whole process.
Upload the following program using the Arduino ISP
programmer then disconnect the programmer.
#include <stdint.h>
#include <avr/io.h>
#include <util/delay.h>
#include "tm1637.h"
#include "tm1637.c"
int
main(void)
{
uint8_t n, k = 0;
/* setup */
TM1637_init(1/*enable*/,
5/*brightness*/);
/* loop */
while (1) {
for (n = 0; n < TM1637_POSITION_MAX;
++n) {
TM1637_display_digit(n, (k +
n) % 0x10);
}
TM1637_display_colon(1);
_delay_ms(200);
TM1637_display_colon(0);
_delay_ms(200);
k++;
}
}
You will see the numbers “0123456789” counting up and scrollimg displayed
on the TM1637 module.
DHT-11 Temperature
and Humidity Sensor
A
very poular sensor that is widely used is the DHT11 humidity sensor. This is a
digital humidity and temperature sensor and has four pins. The measurement
resolution of this module is 1°C or 1% relative humidity. Here, it will just be
used for measuring relative humidity; measurement of temperature is better done with the more
accurate and sensitive LM35 (see previous).
Simply
connect Data pin to ATTiny pin 3 (PB4) and pins to +5V and GND to VCC and GND,
respectively
Upload the following program using the Arduino ISP
programmer then disconnect the programmer.
/**
* Copyright (c) 2019, Łukasz Marcin Podkalicki
<lpodkalicki@gmail.com>
* ATtiny13/030
* Read data from DHT11 sensor and log
temperature and humidity
* using software uart (19200/8N1).
*
* Settings:
*
FUSE_L=0x6A
*
FUSE_H=0xFF
*
F_CPU=1200000
*/
#include
<avr/io.h>
#include
<util/delay.h>
//#define
DHT_PIN PB0 //physical pin 5
#define
DHT_PIN PB4 //physical pin 3
#define
DHT_ERR_OK (0)
#define
DHT_ERR_TIMEOUT (-1)
#define
DHT_PIN_INPUT() (DDRB &=
~_BV(DHT_PIN))
#define
DHT_PIN_OUTPUT() (DDRB |= _BV(DHT_PIN))
#define
DHT_PIN_LOW() (PORTB &=
~_BV(DHT_PIN))
#define
DHT_PIN_HIGH() (PORTB |=
_BV(DHT_PIN))
#define
DHT_PIN_READ() (PINB &
_BV(DHT_PIN))
#define
DHT_TIMEOUT (10)
static
void dht_init(void);
int8_t
dht_read(uint8_t *temperature, uint8_t *humidity);
//Serial.begin
(19200);
int
main(void)
{
uint8_t temperature, humidity;
/* setup */
dht_init();
/* loop */
while (1) {
dht_read(&temperature,
&humidity);
Serial.print ("T:");
Serial.print (temperature);
Serial.print (" C, H:");
Serial.print (humidity);
Serial.print ("%\n");
Serial.println ("");
_delay_ms(1000);
}
}
void
dht_init(void)
{
DHT_PIN_INPUT();
DHT_PIN_HIGH();
}
static
int8_t
dht_await_state(uint8_t
state)
{
uint8_t counter = 0;
while ((!DHT_PIN_READ() == state)
&& (++counter < DHT_TIMEOUT)) { _delay_us(1); };
return counter;
}
int8_t
dht_read(uint8_t
*temperature, uint8_t *humidity)
{
uint8_t i, j, data[5] = {0, 0, 0, 0, 0};
/* send start sequence */
DHT_PIN_OUTPUT();
DHT_PIN_LOW();
_delay_ms(20);
DHT_PIN_INPUT();
DHT_PIN_HIGH();
/* read response sequence */
if (dht_await_state(0) < 0 ||
dht_await_state(1) < 0 || dht_await_state(0) < 0) {
return DHT_ERR_TIMEOUT;
}
/* read data */
for (i = 0; i < 5; ++i) {
for (j = 0; j < 8; ++j) {
data[i] <<= 1;
data[i] |= !!(dht_await_state(1)
> 0 && dht_await_state(0) > 1);
}
}
*temperature = data[2];
*humidity = data[0];
return DHT_ERR_OK;
}
Disconnect the ISP programmer and connect
the serial adapter. Open The Serial Monitor at 57600 baud rate.
You
will see that the humidity measurement displayed and continually updated.
If
you breathe on the sensor, you should see the humidity measurement change. Try
altering the delay interval or the way that the data is displayed in the Serial
Monitor. This sensor can be used in conjunction with other sensors/modules as
part of larger projects, for example a weather monitoring station.
Light Sensor
Light
sensors, also known as photo resistors or light dependant resistors (LDR) are
generally quite basic and un-calibrated. In this example, I have used a GL5537
LDR but there are plenty of others that perform equally as well. The LDR
changes its resistance depending on the amount of light and we can measure this
change using one of the ATTiny analog pins.
The
LDR has only two pins so connect one of these pins to GND (ground) and the
other pin to a 10k ohm pull up resistor and to the ATtiny
analog pin 3 (A2) (see Arduino Section
for wiring diagram which can be adapted for the ATTiny). Upload the
following program using the Arduino ISP programmer then disconnect the
programmer. Connect the serial adapter and open the
Serial Monitor.
const
uint8_t INPUT_PIN = A2; //pin 3
void
setup()
{
Serial.begin(57600); // NOTICE the baud rate specified is
ignored on the T13
// Instead it is hard coded as follows...
// Processors at 9.6MHz ==> 57600
// Processors at 4.8 and 1.2MHz ==> 9600
}
void
loop()
{
uint16_t sensorValue = analogRead(INPUT_PIN);
//Serial.println(sensorValue);
Serial.println(sensorValue)*1000/1024;
delay(2000);
}
Placing
your hand (or piece of paper) over the sensor results in a change of values
displayed. The more light, the lower the reading.
As
the measured resistance varies greatly from bright light to darkness, we need
to either use a lookup table or perform calculations on the readings to convert
to lux values.
Below
is a table to reference when changing the ADC reading to lux
Vcc 5V
Pullup
resistor 10000 ohms
|
lux |
ohms |
Voltage |
ADC |
Scaling |
|
0 |
|
1023 |
|
|
|
1 |
200000 |
4.76 |
975 |
-0.020937188 |
|
10 |
30000 |
3.75 |
768 |
-0.043428309 |
|
80 |
5000 |
1.67 |
341 |
-0.1640625 |
|
120 |
4000 |
1.43 |
293 |
-0.8203125 |
|
150 |
3000 |
1.15 |
236 |
-0.533203125 |
|
250 |
2000 |
0.83 |
171 |
-1.5234375 |
|
300 |
1800 |
0.76 |
156 |
-3.45703125 |
|
800 |
700 |
0,33 |
67 |
-5.604580966 |
There
is a program in the Arduino Section that can be adopted for the ATTiny.
The
first program (without the conversion to lux) would be sufficient if you just
want to detect the difference between light and shadow, if a light is on or off
or there is just a change in brightness.
These
inexpensive sensors can be readily bought on the internet. They can be used as
part of larger projects such as monitoring light around the home and switching
lights on and off depending on levels of light detected.
Sound Sensor
A sound sensor can, and is, a very useful
item. It can be used to trigger lights, doors, alarms etc or even used to
control a smart vehicle.
The sensor detects sound through a
microphone and this sound is passed through a LM393 op amp. The particular one
used here has an operating voltage of 3.3V-5V and has a signal output indicator
LED which comes on when a sound is detected. The output is low when there is
sound and high when there is no sound (i.e. LOW and HIGH pin states
respectively.
The three pin version of this module
connects to the ATTiny as follows:
VCC 5V
GND GND
OUT ATTiny
pin 2 (PB2)
As the output is
digital, we can only detect when there is sound detected or not. If we wanted
to detect relative levels of sound, we would need a module that has an analog
output.
Connect the module to your ATTiny as
above and then. Upload the following program using the Arduino ISP
programmer then disconnect the programmer.
const uint8_t
SOUND_PIN = 2; //PB2, physical pin 7
void
setup()
{
Serial.begin(57600);
}
void
loop()
{
uint8_t noiseState = digitalRead(SOUND_PIN);
Serial.println(noiseState);
delay(200);
}
Connect the serial adapter and open the Serial Plotter and make some noise. You will see that the output changes as the sensor
detects the sounds. You may need to alter the sensitivity by adjusting the
onboard potentiometer.
By making the
delay shorter, the system will become more responsive.
Flame Detection
Sensor
The 4 pin flame
detection sensor is generally an infrared (IR) receiver which can detect IR
radiation between 760 nm and 1100 nm.
To test the
module, a flame should be set of approximately 80cm from the sensor. If using a
flame larger than that given by a lighter, this will be detected at a greater
distance.
The module has a
detection angle of 60 degrees and sensitivity is adjustable using the on-board digital
potentiometer. It has an operating voltage of 3.3V to 5V and outputs of both
digital and analog. Here is the pinout of the module:
VCC:
VCC
GND:
GND.
DO:
TTL switch signal output.
AO:
Analog Output.
Connect the analog pin of the module to
the ATtiny analog pin A2 (physical
pin 3 of the ATTiny, see core diagram) as well as GND to GND and VCC to VCC. Upload the following program using the Arduino ISP
programmer then disconnect the programmer
const
uint8_t INPUT_PIN = A2; //physical
pin 3
void
setup() {
Serial.begin(57600);
}
void
loop() {
uint16_t
sensorValue = analogRead(INPUT_PIN);
Serial.print("Flame Reading
");
Serial.print(sensorValue); // print out the value
Serial.println (" ");
delay(1000); // delay between readings for
stability
}
Disconnect
the ISP programmer and connect the serial adapter and
open the Serial Monitor. Pass a
Close the Serial Monitor and open the
Soil Moisture
Sensor
This sensor measures
the volumetric content of water in the soil and gives the moisture level as output. The sensor is equipped with both analog and digital output, so it
can be used in both analog and digital mode.
Connect the analog pin of the module to the ATtiny analog pin 3
(A2) .
Upload
the following program using the Arduino ISP programmer then disconnect the
programmer
const
uint8_t INPUT_PIN = A2; //pin 3 of
the ATTiny
void
setup() {
Serial.begin(57600);
}
void
loop() {
int16_t
sensorValue = analogRead(INPUT_PIN);
int
sensorValue = analogRead(INPUT_PIN)/10;
// read the input on analog pin
A2 and divide the value by 10
Serial.print("Moisture Reading
");
Serial.print(sensorValue); // print out the value
Serial.println (" ");
delay(2000); // delay between readings for
stability
}
Connect the serial adapter and open the Serial Monitor.
Though
the accuracy of these sensors is questionable, they can still be used in
various projects that require soil moisture level data. An advanced project maybe to monitor the
moisture level in a plant pot and if the level falls below a certain level, you
could trigger a pump to deliver a certain amount of water. This would be a good
way of caring for you houseplants whilst on holiday.
Ultrasound Distance Measurement
The HC-SR04 ultrasonic sensor uses sonar to determine distance to an
object. It offers excellent non-contact range detection with high accuracy and
stable readings. The range is from 2cm to 400 cm (1” to 13 feet).
There
are four pins on the sensor module and these should be connected as follows:
HC
SR04 ATTiny
VCC 5v
GND GND
Echo
pin
3 (physical pin 2)
Trig pin
4 (physical pin 3)
The
sketch was compiled from referencing many similar sketches that are available
in the public domain. Upload the following program using the Arduino ISP
programmer then disconnect the programmer
const
uint8_t echoPin = 3;
//(physical pin 2)
const
uint8_t trigPin = 4; //(physical pin 3)
long
duration, distance;
void
setup() {
Serial.begin (57600);
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
}
void
loop() {
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
duration = pulseIn(echoPin, HIGH);
distance = duration/58;
Serial.println(distance);
delay(500);
}
Connect the serial adapter and open the Serial Monitor. By
moving an object to various distances in front of the detector, the distance
displayed changes as in the screen shot below.
The
ATTiny is not very fast and the result of this is that reading are missed
(hence the zeros). Changing the pauses (
Using
this core (Core 2), this sketch uses 912 bytes (89%) of program storage space.,
where the maximum is 1024 bytes. If the
true factor to calculate distance was to put into the program (distance =
duration/58; rather than distance = duration/58.;), the program would be too
large to upload. In fact, the program would be 1798 bytes (175%) of program
storage space before getting rid of this floating point. Ways around this
storage problem bur still correctly (and
accurately) calculating the distance, include using one of the following:
distance
= (duration*10)/582; This can be done by
using int (or long), but no rounding will be done.
distance
= (((duration*100)/582)+5)/10; This can be done by using int (or long), with an
imperfect 1 digit rounding.
distance
= (((duration*1000)/582)+50)/100; // This can be done by using int (or long),
with an imperfect 2 digit rounding.
That
said, dividing by 58 is totally fine. This
is simply within the margin of error, the result is still just +/- 1cm difference
from the actual distance according to data supplied by James Sleeman:
The
HC SR04 Ultrasonic Sensor can be used in many projects. One that springs to
mind would be to use it in a model car or robot as part of a collision
avoidance feature.
Potentiometer
A potentiometer is a simple knob/dial that provides a
variable resistance. The result of varying the resistance in a system can then
be read by the ATTiny as an analog value. These
usually have only three pins; analog in, VCC and GND. The one used in the
example below is rated at 10k ohm.
The circuit set up for
the following example is quite simple. Connect the
analog pin (centre pin) of the module to the ATtiny analog pin 3 (A2).
Upload
the program using the Arduino ISP programmer then disconnect the programmer
const
uint8_t INPUT_PIN = A2; //pin 3
void
setup()
{
Serial.begin(57600);
}
void
loop()
{
uint16_t sensorValue = analogRead(INPUT_PIN);
//Serial.println(sensorValue);
Serial.println(sensorValue)*1000/1024;
delay(2000);
}
Once
the sketch is uploaded, disconnect the ISP programmer and connect the serial adapter. Open the Serial Monitor.
If
you turn the knob on the potentiometer you will see the voltage values change
either up or down depending on whether you turn the knob clockwise or anticlockwise.
Once
you are more experienced and building projects that contain several components,
I’m sure that you will find the need to use at least one potentiometer.
Data Logging:
MakerPlot
MakerPlot is Windows® software for
plotting analog and digital data generated by microcontrollers and other
devices with ASCII serial outputs. No proprietary hardware is required –
just a serial connection from the microcontroller or other device to the
PC.
With
dozens of meters, buttons, switches and text boxes the user has full control of
the screen interface design and how information is displayed, plotted, logged
to files, printed and more.
Your
microcontroller also has the ability to read information directly from the
interfaces for interactive measurement, plotting and control.
Full details and how to
purchase, along with how to download a free trail of MakerPlot can be found
here (though once tried, you will probably want to purchasing it):
http://makerplot.com/home.html
After
downloading and launching MakerPlot, the user is greeted with a myriad of
applications. If they look closer there is a button to access even more on a
second page.
There
is a very comprehensive guide and ten Arduino code samples accessible from the
main interface.
There
are well over a dozen applications available but for the purpose of this work,
only a couple of them will be explored. If you download MakerPlot, I’m sure
that you will have hours of enjoyment trying out the various applications.
Below
are a couple of screenshots that show the applications that are available:
To test this software, connect an LM35 precision analog
temperature sensor as earlier in this section, remembering that the central pin
should be connected directly to pin 3 (PB4) of the ATTiny.
Upload the following sketch via the Arduino ISP programmer.
const uint8_t INPUT_PIN = A2; //pin 3
void setup()
{
Serial.begin(57600);
}
void loop()
{
uint16_t sensorValue =
analogRead(INPUT_PIN);
Serial.println
(sensorValue)*1000/1024*5000;
delay (2000);
}
As mentioned before, when the ATTiny is using the 5V reference
with 10bit analog to digital conversion (ADC) it has a 1024 count. Therefore,
we have to perform the following calculation on the sensor measurement to give
true temperature in Centigrade.
Temprature = (sensorValue)*1000/1024*5000, i.e. temp = Vin * 1000
/ 1024 * Vref [milivolts]
Disconnect the ISP programmer and connect the seial adapter.
Connect
your ATTiny to the computer via a USB cable and launch the MakerPlot
application. From the first screen, select “Run Standard Interface”.
.
We
will be collecting temperature data every 2 seconds (2000 milliseconds) so we
need to alter the axes accordingly. I set the x-axis to 60 seconds and the
y-axis to 100. Now select the port and baud rate (56000 for ATTiny). Note the
preferred baud rate of 57600 is not available but 56000 works in this example.
Select
“Log To File” in the bottom panel. MakerPlot is now ready to receive the sensor
data.Click the Control Switch to begin data acquisition.
Open
the std_dat.txt file to see the raw data. Here is a screenshot of a portion of
my raw data file:
You
can save this file and then import into Excel for some further analysis/plotting.
I
have only briefly covered a small part of this software package. I’m sure that
you will experiment with the other applications within it.
NE555 Frequency
Adjustable Pulse Generator
This is a square
wave pulse generator module based on the ubiquitous NE555 timer IC. The module
can be powered from a wide power supply range (4.5 to 12V) and will produce a
continuous square wave output between 15Hz to 24KHz which can be set by on
on-board multi-turn potentiometer.
Connect central pin of the NE555 module to pin 3 (PB4) of the
ATTiny, the GND to GND and VCC to VCC.
Upload the following sketch via the Arduino ISP programmer
const uint8_t INPUT_PIN = A2; //pin 3
void setup()
{
Serial.begin(57600);
}
void loop()
{
int16_t sensorValue =
analogRead(INPUT_PIN);
Serial.println (sensorValue)*1000/1024*5000;
delay (200);
}
Disconnect the ISP programmer and connect the seial adapter.
Connect
your ATTiny to the computer via a USB cable and open the Serial Monitor.
To
view the data graphically, open the Serial Plotter. The Serial Plotter is
limited and the resulting plot is difficut to interpret but you can just about
see the pattern of the pulse generated from the module.
LM386 Mono Class D Amplifier
The
LM386 is a low voltage power amplifier with an inactive power draw of 24mW,
which makes it suitable for battery controlled applications.
The
project runs on ATtiny13 with maximum internal clock source (9.6MHz). This
example gives the posibility to use the maximum of hardware PWM frequency (Fast
PWM mode).
The PWM freqency of class D amplifiers should be of
course much higher, a hundreds of kHz (some sources says 8 times more than
sampling frequency). Unfortunately, in case of ATtiny13 it’s not posible to
achieve such parameters. However, for experimentally selected settings of ADC
sampling rate (~10kHz) and PWM frequency (37.5kHz) the amplifier sounds very
good.
I used a cable which had 3.5mm jack plugs on each end.
One end goes into my laptop and I cut the other end to reveal three wires, The
blue wire connects to pin 2 of the ATTiny (PB3), the red wire to VCC and the
black wire to GND. The terminal connectors of the LM386 module connect GND to
black and OUT to red of the external speaker. The IN pin of the LM386 module
connects to pin 5 (PB0) of the ATTiny, the two GND pins connect to GND and the
VCC connects to VCC.
The code is very short and uses a lot of hardware settings which has
been explained line-by-line in the comments (thanks to Łukasz Marcin
Podkalicki). Upload the following sketch via the
Arduino ISP programmer
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#define
AUDIO_IN PB3 //Physical pin 2
#define AUDIO_OUT PB0 //Physical pin 5
int
main(void)
{
/* setup */
DDRB |=
_BV(AUDIO_OUT); // set AUDIO_OUT pin as output
TCCR0A |=
_BV(WGM01)|_BV(WGM00); // set timer mode to FAST PWM
TCCR0A |=
_BV(COM0A1); // connect PWM pin to channel A
TCCR0B |=
_BV(CS00); // set prescaler to 1
ADMUX |=
_BV(MUX0)|_BV(MUX1); // select ADC3 (PB3)
ADMUX |=
_BV(REFS0); // set internal 1.1V reference voltage
ADMUX |=
_BV(ADLAR); // left adjust of ADC result
ADCSRA |=
_BV(ADPS1)|_BV(ADPS0); // set ADC division factor to 8
ADCSRA |= _BV(
ADCSRA |=
_BV(ADATE); // set ADC auto-trigger
ADCSRA |=
_BV(ADSC); // start conversion
sei(); //
enable global interrupts
/* loop */
while (1);
}
ISR(ADC_vect)
{
OCR0A = ADCH;
// the magic
}
Nokia 5110 LCD
Display
The
Nokia 5110 84x48 LCD graphics display was used on millions of mobile phones in
the late 1990's and due to their simple connections they are easily integrated
into electronics projects. They are inexpensive, versatile and are very
reliable. They are available “as is” or can be purchased with header pins
already attached.
Nokia 5110 LCD Module
To
demonstrate the 5110 module, the following example collects data from an LM35
temperatyre sensor and displays the temperature on the 5110 screen. For TMP36
sensor ( which is capable to measure -50 - 100 Celsius ) there is a correction
in the code to substract 0.5V from ADC reading. So if you want to use LM35
sensor you need to remove this line in the code.
Follow
the pin out schemes below
LM35 (flat face facing) ATTiny
Data
pin (centre) pin
2 (PB3)
VCC
(left) VCC
GND
(right) GND
Nokia 5110 ATTiny
RST 5 (PB0)
CE GND
DC 6 (PB1)
DIN 7 (PB2)
CLK 3 (PB4)
VCC 3V
LIGHT GND
(for the backlight)
GND GND
Upload the following sketch via the Arduino ISP programmer
#include <avr/io.h>
#include
<avr/pgmspace.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include <avr/sleep.h>
#include
<util/delay.h>
#include <avr/wdt.h>
#include
<avr/interrupt.h>
/** Glyph data (stored in
PROGMEM)
*/
extern const uint8_t
SMALL_FONT[] PROGMEM;
// SMALL FONTS only fit into
progmem
const uint8_t SMALL_FONT[]
PROGMEM = {
0x3e, 0x51, 0x49, 0x45, 0x3e, // 30 0
0x00, 0x42, 0x7f, 0x40, 0x00, // 31 1
0x42, 0x61, 0x51, 0x49, 0x46, // 32 2
0x21, 0x41, 0x45, 0x4b, 0x31, // 33 3
0x18, 0x14, 0x12, 0x7f, 0x10, // 34 4
0x27, 0x45, 0x45, 0x45, 0x39, // 35 5
0x3c, 0x4a, 0x49, 0x49, 0x30, // 36 6
0x01, 0x71, 0x09, 0x05, 0x03, // 37 7
0x36, 0x49, 0x49, 0x49, 0x36, // 38 8
0x06, 0x49, 0x49, 0x29, 0x1e, // 39 9
0x08, 0x08, 0x08, 0x08, 0x08 // 2d -
};
// Nokia LCD pin numbers
#define LCD_MOSI PINB2
#define LCD_SCK PINB4
#define LCD_CD PINB1
#define LCD_RESET PINB0
/** Number of columns */
#define LCD_COL 84
/** Number of text rows */
#define LCD_ROW 6
/** Transfer data to a slave
(MSB first)
*
* @param sck the pin to use for the SCK output
* @param mosi the pin to use for the MOSI
output
* @param data the data to transfer
* @param bits the number of bits to transfer
*/
void sspiOutMSB(uint8_t sck,
uint8_t mosi, uint16_t data, uint8_t bits) {
uint16_t mask = (1 << (bits - 1));
uint8_t output = (1 << mosi);
uint8_t clock = (1 << sck);
while(bits) {
// Set data
if(data&mask)
PORTB |= output;
else
PORTB &= ~output;
// Bring the clock high
PORTB |= clock;
// Move to the next bit
mask = mask >> 1;
bits--;
// Bring the clock low again
PORTB &= ~clock;
}
}
/** Send a data byte to the
LCD
*
* @param data the data byte to send.
*/
void lcdData(uint8_t data) {
// Bring CD high
PORTB |= (1 << LCD_CD);
// Send the data
sspiOutMSB(LCD_SCK, LCD_MOSI, data, 8);
}
/** Send a command byte to
the LCD
*
* @param cmd the command byte to send.
*/
void lcdCommand(uint8_t cmd)
{
// Bring CD low
PORTB &= ~(1 << LCD_CD);
// Send the data
sspiOutMSB(LCD_SCK, LCD_MOSI, cmd, 8);
}
/** Write a single character
*/
void lcdPrintChar(uint8_t
row, uint8_t col, uint8_t ch) {
// Set the starting address
const uint8_t *chdata = SMALL_FONT + (ch *
5);
lcdCommand(0x80 | col);
lcdCommand(0x40 | (row % LCD_ROW));
// And send the column data
for(uint8_t pixels = 0; pixels < 5; pixels++,
chdata++) {
uint8_t data = pgm_read_byte_near(chdata);
lcdData(data);
// for double sized font put
// lcdData(data);
};
// Add the padding byte
// if(col < LCD_COL)
lcdData(0x00);
}
/* MAIN PROGRAM
*/
// Watchdog generated
interrupt in program
// used for polling ADC data
and displaying LCD info
//
ISR(WDT_vect) {
// while interrupt do ADC and send output to
LCD
int temp;
uint8_t firstdig;
uint8_t seconddig;
uint8_t low, high;
ADMUX = _BV(MUX0)|_BV(MUX1); // ADC3
ADMUX &= ~_BV(REFS0); // explicit
set VCC as reference voltage (3V = 3000mV)
// ADMUX |= _BV(REFS0); // 1.1 V reference
voltage by setting 1 to bit REFS0
ADCSRA |= _BV(
ADCSRA |= _BV(ADSC); // Run single conversion
while(bit_is_set(ADCSRA, ADSC)); // Wait
conversion is done
// Read values from ADC
low = ADCL;
high = ADCH;
// clear nokia LCD
lcdCommand(0x80);
lcdCommand(0x40);
// Fill in the whole display with ZEROS
for(uint16_t index = 0; index < (LCD_COL
* LCD_ROW); index++)
lcdData(0x00);
// measure voltage from LM35 with VREF
= 3000 mV
temp = (
( ((high << 8) | low) *
3000UL) >> 10);
// measure with internal Vref 1.1V,
but it is not reliable when powering down
// temp = ((((high << 8) | low) * 1100UL)
>> 10);
//
correction for LM36 - TMP36 readings -0,5V
if (temp > 499) {
temp = temp - 500;
}
else
{
// display MINUS sign if
temperature below zero
temp = 500 - temp;
lcdPrintChar(2, 30,
10);
}
firstdig = temp / 100;
// seconddig = (temp - (firstdig * 100))/
10;
seconddig = temp %10;
lcdPrintChar(2, 40, firstdig);
lcdPrintChar(2, 47, seconddig);
// ADCSRA &= ~(1<<
}
int main(void)
{
DDRB &= ~_BV(PB3); // set data pin
as ADC INPUT
// LCD INITIALIZATION
/** Initialise the LCD
*
* Sets up the pins required to communicate
with the LCD screen and then does
* the actual chipset initialisation.
*/
// Set up the output pins, ensuring they are
all 'low' to start
uint8_t val = (1 << LCD_SCK) | (1
<< LCD_MOSI) | (1 << LCD_RESET) | (1 << LCD_CD);
PORTB &= ~val;
DDRB |= val;
// Do a hard reset on the LCD
//_delay_ms(10);
PORTB |= (1 << LCD_RESET);
// Initialise the LCD
lcdCommand(0x21); // LCD Extended Commands.
lcdCommand(0xA1); // Set LCD Vop (Contrast).
lcdCommand(0x04); // Set Temp coefficent.
lcdCommand(0x14); // LCD bias mode 1:48.
lcdCommand(0x20); // LCD Normal commands
lcdCommand(0x0C); //
//set timer to 1 sec for measurements
of LM35
WDTCR |= (0<<WDP3) |
(1<<WDP2) | (1<<WDP1) | (0<<WDP0);
// set timer to 0.5s
// WDTCR |= (1<<WDP2) | (1<<WDP0);
// set timer to 4 sec
// WDTCR |= (1<<WDP3);
// Set watchdog timer in interrupt
mode
WDTCR |= (1<<WDTIE);
WDTCR |= (0<<WDE);
sei(); // Enable global interrupts
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
for (;;) {
sleep_mode(); // go to sleep and wait for interrupt...
}
}


No comments:
Post a Comment