Attiny85 Buzzer doesn't work.(Annoy-a-tron Clone)

Hi,
I am working on a project based on "Annoy-a-tron" device just like here.

I wanted a cricket sound instead of pwm generated single tone and modified the original code:

#include <Arduino.h>
#include <avr/io.h>
#include <avr/sleep.h>
#include <avr/wdt.h>
#include <stdlib.h>
#include <util/atomic.h>
#include <util/delay.h>

// PIN CONFIGURATION:
// BUZZER - B4  (OC1B)
//    NOTE: Buzzer needs to be connected to Vcc & B4 (NOT ground) since we are
//          using the internal pull-ups on GPIOS to reduce power.  If it's on
//          GND, we'd need a pull-down instead which is an extra component.
//          Since the buzzer runs on AC, I think it should be just fine.
// DELAY JUMPER - B0 --- SWITCH/JUMPER -- 10kOhm --- B3
//    NOTE: This jumper/switch needs to lie on a trace connecting these two
//          GPIOS with a current limited resistor in series.  This system, as
//          opposed to a traditional 1-GPIO switch circuit saves lots and lots
//          of power.

#define RANDOM_SEED 0x8F
#define MIN_INTERBEEP_DELAY_S (60 * 1)  // Approximately 15 minutes
#define MAX_INTERBEEP_DELAY_S (60 * 4)  // Approximately 45 minutes
#define NUM_INITIAL_BEEPS_NODELAY 4  // +1 additional beep for the first regular beep
#define NUM_INITIAL_BEEPS_DELAYED 50
#define INITIAL_TIME_DELAY_S (14UL * 24UL * 60UL * 60UL) // Approximately 2 weeks
#define BEEP_DURATION_MS 20

#define SLEEP_DURATION_S 8 // This is set by the WDT prescalar in enableWDTInterrupt()
#define MIN_SLEEPS_BETWEEN_BEEPS (MIN_INTERBEEP_DELAY_S / SLEEP_DURATION_S)
#define MAX_SLEEPS_BETWEEN_BEEPS (MAX_INTERBEEP_DELAY_S / SLEEP_DURATION_S)
#define NUM_INITIAL_SLEEPS_FOR_DELAY (INITIAL_TIME_DELAY_S / SLEEP_DURATION_S)

const int buzzerPin = PB4;     // Passive buzzer on PB4 THIS IS ADDED

uint32_t sleeps_until_next_beep = 0;
uint8_t is_delay_jumper_connected;

static void setAllGPIOAsInputs() {
  // Configure all 6 GPIO pins as input (as a default and/or to save power)
  DDRB &= ~(_BV(PB0) | _BV(PB1) | _BV(PB2) | _BV(PB3) | _BV(PB4) | _BV(PB5));
  // Turn on the internal pullups for each of them as well.
  PORTB |= _BV(PB0) | _BV(PB1) | _BV(PB2) | _BV(PB3) | _BV(PB4) | _BV(PB5);
}

static void setupOutputGPIOs() {
  // Configure PB4 as an output (it'll be the PWM OC1B)
  DDRB |= _BV(PB4);
}

static void initPWM() {
  // Enable PWM output on timer1 b and configure how it'll work
  GTCCR = (0x01 << PWM1B) | (0x02 << COM1B0);

  // Set up the clock prescalar and reset point (configure period)
  // The main clock is running at 1Mhz you can compute the new period (in ms) like so:
  //  1000 / ((1000000 / PRESCALAR) / OCR1C)
  OCR1C = 255;
  //TCCR1 = (0x07 << CS10); // Prescalar = 1:64, so the period is 16.4ms
  TCCR1 = (0x01 << CS10); // Prescalar = 1:1, so the freq is about 4000Hz
}

static void inline disablePWM() {
  GTCCR = (0x00 << PWM1B) | (0x00 << COM1B0);
}

static void inline SetPWMOutput(uint8_t duty) {
  OCR1B = duty;
}

//BELOW CODE IS MODIFIED
static void beep() {
  setupOutputGPIOs(); // Turn the buzzer's GPIO into an output

  // Triple chirp with varying frequencies for realism
  for (int chirp = 0; chirp < 5; chirp++) {
    // Sweep frequency up for natural sound (4500-5000Hz)
    for (int freq = 2000; freq <= 4000; freq += 100) {
      tone(buzzerPin, freq, 5);  // 4ms pulses
      delay(2);                  // Creates frequency sweep
    }
    delay(30 + chirp*10);  // Increasing delay between chirps
  }
  
    delay(200);  // Debounce
	  // Triple chirp with varying frequencies for realism
  for (int chirp = 0; chirp < 4; chirp++) {
    // Sweep frequency up for natural sound (4500-5000Hz)
    for (int freq = 2000; freq <= 5000; freq += 100) {
      tone(buzzerPin, freq, 2);  // 4ms pulses
      delay(1);                  // Creates frequency sweep
    }
    delay(30 + chirp*10);  // Increasing delay between chirps
  }
  noTone(buzzerPin);
    delay(7000);  // Debounce

  setAllGPIOAsInputs(); // Turn all GPIOs back into inputs to save power
}

static void adcDisable() {
  // This function disables the ADC.  It uses a lot of power, so we turn it off
  // since it's unused in this project
  ADCSRA &= ~(1<<ADEN);
}

//Sets the watchdog timer to wake us up, but not reset
//0=16ms, 1=32ms, 2=64ms, 3=125ms, 4=250ms, 5=500ms
//6=1sec, 7=2sec, 8=4sec, 9=8sec
static void enableWDTInterrupt(uint8_t timerPrescaler) {
  uint8_t WDTCSR_ = (timerPrescaler & 0x07);
  WDTCSR_ |= (timerPrescaler > 7) ? _BV(WDP3) : 0x00; // Set WDP3 if prescalar > 7 (ie. 4.0s, 8.0s)
  WDTCSR_ |= _BV(WDIE); // Enable watchdog interrupt

  //This order of commands is important and cannot be combined (beyond what they are below)
  MCUSR &= ~_BV(WDRF); // Clear the watch dog reset

  // timed sequence
  ATOMIC_BLOCK(ATOMIC_FORCEON)
  {
    WDTCR |= _BV(WDCE) | _BV(WDE); // Set WD_change enable, set WD enable
    WDTCR = WDTCSR_; // Set new watchdog timeout value & enable
  }
}

// Send the whole system into deep sleep until the next WDT interrupt
static void sleepUntilWDTWake() {
  // For maximum power savings the ADC definitely should be disabled as well.  However for this
  // project we never need the ADC so it's just disabled once at the beginning of execution and never
  // touched again.  If you're actually using the ADC you'll have to disable and re-endable it each
  // time you sleep -- it makes a huuuge difference in power consumption.
  set_sleep_mode(SLEEP_MODE_PWR_DOWN); // Configure what kind of sleep to use (very low power mode)
  sleep_enable();                      // Make it possible to go to sleep
  wdt_reset();                         // Reset the WDT to 0 to get the full delay before the interrupt fires
  sleep_mode();                        // Actually send the system to sleep
  sleep_disable();                     // System continues execution (wakes) here when watchdog times out
}

// Watchdog Interrupt Service / is executed when watchdog timed out.
// It doesn't have to really do anything because just by firing this interrupt
// execution is restarted and the system is woken up.
ISR(WDT_vect) {}

void checkTimeDelayJumper() {
  // This function runs immediately on boot, so it assumes everything is unconfigured.  It works
  // because PB0 and PB3 are connected to one another with a 10k resistor between them.  Here we
  // configure one as an input and one as an output and quickly check if the wire is still connecting
  // them by changing the value on the output and checking it at the input.  If the input value
  // matches the output one, then we know the jumper has not been cut.

  // Configure PB0 as an output
  DDRB |= _BV(PB0);
  // Configure PB3 as an input w/ pull-up (to detect the state)
  DDRB &= ~_BV(PB3);
  PORTB |= _BV(PB3);

  // Turn the output pin to 0, if they're connected, PB3 should read a 0
  PORTB |= _BV(PB0);
  // Then read in the value on PB3, and see the state of the jumper.  If it reads 0 (like PB0 is set
  // to) then we know the jumper is still connected.  Otherwise someone cut the jumper
  is_delay_jumper_connected = !(PINB & _BV(PB3));
  // Turn PB0 back into an input to save power.
  DDRB &= ~_BV(PB0);
}

int main (void) {
  // Very first thing to do is check if the time-delay jumper is set.  This sets the global
  // flag "is_delay_jumper_connected" for use in the rest of the program, and is only run once
  // on boot.  The system must be reset to get a new reading.
  checkTimeDelayJumper();
  if (is_delay_jumper_connected) {
    // If the delay jumper is connected, we start off requiring a huge number of sleeps before
    // the first beep is ever played -- thus adding a time-delay feature.
    sleeps_until_next_beep = NUM_INITIAL_SLEEPS_FOR_DELAY;
  }

  // TODO: Get a better random seed?  It's silly, but it's a reasonably easy thing to improve
  srand(RANDOM_SEED);  // To get a seemingly random pause length we need to seed the RNG
  adcDisable();  // We never use the ADC, so it should be immediately disabled for power savings
  enableWDTInterrupt(9); // Set up the WDT to run as slowly as possible (approx 8s)

  // Play a short burst of beeps on startup, so the user knows everything is working
  uint8_t num_initial_beeps = is_delay_jumper_connected ? NUM_INITIAL_BEEPS_DELAYED : NUM_INITIAL_BEEPS_NODELAY;
  for (uint8_t i = 0; i < num_initial_beeps; i++) {
    beep();
  //  _delay_ms(BEEP_DURATION_MS);  // Put an equal-length pause between beeps
  }

  // Main loop of the program.  It waits for a randomly selected # of low power "sleeps" in
  // between playing short beeps.
  while (1) {
    beep();
    if (!sleeps_until_next_beep--) {
      // Determine how long the next delay will be
      sleeps_until_next_beep = rand() % (MAX_SLEEPS_BETWEEN_BEEPS - MIN_SLEEPS_BETWEEN_BEEPS) +
                               MIN_SLEEPS_BETWEEN_BEEPS;

      // Do the actual beeping!
      beep();
    }

    // Go into the lowest power mode for a while (saving battery while we wait)
    sleepUntilWDTWake();
  }

  return 1;
}

As you can see i only modified "beep()" function but my code doesn't work.

To isolate issue i used below simple code it works without issues. But i want low power features and 2 week delay option also which are implemented on original code.

This code generates cricket sound perfectly:

#include <Arduino.h>
#include <avr/io.h>
#include <avr/sleep.h>
#include <avr/wdt.h>
#include <stdlib.h>
#include <util/atomic.h>
#include <util/delay.h>


const int buzzerPin = PB4;     // Passive buzzer on D8


void setup() {
  pinMode(buzzerPin, OUTPUT);
}

void loop() {
  // Triple chirp with varying frequencies for realism
  for (int chirp = 0; chirp < 5; chirp++) {
    // Sweep frequency up for natural sound (4500-5000Hz)
    for (int freq = 2000; freq <= 4000; freq += 100) {
      tone(buzzerPin, freq, 5);  // 4ms pulses
      delay(2);                  // Creates frequency sweep
    }
    delay(30 + chirp*10);  // Increasing delay between chirps
  }
  
    delay(200);  // Debounce
	  // Triple chirp with varying frequencies for realism
  for (int chirp = 0; chirp < 4; chirp++) {
    // Sweep frequency up for natural sound (4500-5000Hz)
    for (int freq = 2000; freq <= 5000; freq += 100) {
      tone(buzzerPin, freq, 2);  // 4ms pulses
      delay(1);                  // Creates frequency sweep
    }
    delay(30 + chirp*10);  // Increasing delay between chirps
  }
  noTone(buzzerPin);
    delay(7000);  // Debounce
}

I am using visual studio code with platformio and here is my platformio ini file:

[env:attiny85]

platform = atmelavr

board = attiny85

framework = arduino

upload_protocol = micronucleus

Here is my schematic:

Can anyone point me a direction?

Thanks in advance.

Nice post! I am not sure what the beeper is, they come as miniature piezo speakers or devices that you just add DC and they beep. Place a few volts across the two pins, if it beeps it is a beeper if it clicks it is a speaker. They look the same but are not interchangeable in most applications.

That's a whole lotta code for an annoyatron...

... even for a mostBedazzlingngAnnoyatron.

@gilshultz
@xfpd

Many thanks for reply. My code above for cricket sound works ok. You can see it on here.
However i want:

  • Low power features (Wathdog and adc disable etc.) to extend battery life.
  • Initial 15 days delay with a switch.
  • Realistic natural cricket sound.
  • Random sound playing.

Buzzer is very low power, however if you watched my video above it is very satisfactory sounding which i think fine.

Example "tinyAnnoyatron" code is lack of above functions. I want to hide this device to somewhere to prank people. It aims stealth mode until someone finds it :slightly_smiling_face:

Totally different from your topic title.

A few things:

You should learn to use the internet. Everything you ever thought of is on the internet. If you have a device (or just a video), the information is on the internet. Take time to search.

The world does not like finding timed, electro-mechanical devices in random places, even (especially) when clearly labeled "prank." Be aware, be understanding, and be careful. Repeating "prank" in the text and code variables does not change what could happen when your device is found by a passerby.

Mechanical-DIP-switch-timing devices are programmable from seconds to months (millions of seconds) that can connect and disconnect power.

ATtiny85 does have sleep. ATtiny85 sleep modes tutorial - Gadgetronicx

My "annoyatron" examples were to show ATtiny85 has PWM capability, because your title says it does not, even when your video shows it working.

The whole project is easily found. arduino-volume1/examples/volume_crickeduino_prank at master · connornishijima/arduino-volume1 · GitHub

Your "natural sound" is un-natural. It's a sawtooth wave. Sounds like static discharge.

Wow easy partner.

First of all, i am not an fluent English speaker or writer, just keep in mind. Because it seems i didn't express myself enough.

Ok then lets just to be clear, my modified first code doesn't work. Second one works as you can see.

What do you mean by that? Does my code wrong? If so what part of that? Now I'm saying that my first code doesn't work and i don't know why. I am here just to work it out. Nothing more.

How do you know that and how do you judge it? Who labels it as "prank"? There is no malicious intend from my side. As far as i can say this information is all public domain. Anyone can build and make this device, right? What happens if my device found by a passerby? Are you accountable/repsonsible for it?

Thanks, i will look into it.

Sorry for that i couldn't express myself and my post title, my mistake. As you said PWM and TONE works.

Thanks again i will look into it.

I don't agree with that, since its subjective and good enough for me.

My intention is not get someone mad or angry. I just want that to work it thats all. If my previous comment offended you i am sorry.

Anglophones would say, "Whoa! Easy, partner," from old-west movies. Everyone here understands non-standard English. "does work" and "does not work" are not misunderstandings... they are polar opposites. Thank you for clearing that up.

  1. Remove all devices, leaving the buzzer connected to the ATtiny85.
  2. Remove all code, leaving the buzzer.
  3. Does the buzzer work? If no, go to #4.
  4. Write code for the buzzer (search for cricket sounds, or experiment).
  5. Write code for "sleep" (search for it, or start with the link provided).

You did. The author of the code and project (linked) did.

I understand. Others might not want to find a remotely timed device counting down to "unknown." Just keep the times in mind and be careful.

Safety is everyone's business. You can not put fear into someone's mind, no matter how you feel about it.

That is all you need. If you like it, it is good.

In the future, you could try to digitize a low-quality, live chirp, and play that back... the limit would be memory of the device. An ESP32-C3 mini is four times the size and price as ATtiny85, but has memory to hold a good chirp.

Yes it's from "The Ballad of Buster Scruggs" if you watched it. Just don't be mean by "Thank you for clearing that up.". Be kind, i didn't disrespect you, however you do.

You are not helping by that. You are writing just for the sake of writing.

So what? Did i harm anybody? Have I hurt someone? Or made some jokes like on YouTube? No. I don't think you should take everything so seriously.

What kind of safety issue are we talking about? Did I put a device that makes a grasshopper sound in someone's car and cause an accident? If a random grasshopper comes up to you while you're walking down the street, is that a safety issue for you too?

Finally, a constructive conversation, but when I haven't even invented or managed to operate a car yet, describing a spaceship to me isn't really helpful.

I really don't understand what you're upset about, and instead of helping me, you keep bringing up different arguments. As I mentioned in my previous post, I tried to be respectful and apologized to you, but I still see that you're far from being constructive. I won't argue with you any further.

We are speaking two different languages.