Friday, April 1, 2022

Electronic Cricket Prank

Courtesy of my prankster daughter, I came downstairs to find Bot Thoughts labs "decorated" for April Fool's Day...

 

desk covered in balloons and streamers

Clearly, I had no choice but to exact revenge. To that end, I will be hiding an electronic cricket in her room to chirp at random, lengthy intervals. But where might one find an electric cricket on short notice? By repurposing a little device I built. Read on...

Fortunately, I happen to be in the middle of redesigning my Lost RC Airplane Finder for ultra-micro airplanes and have one assembled and ready to rock.

prototype lost model finder/alarm
Lost Model Alarm R0.4 Prototype


This little beeper is tiny (the board is the size of a penny), powered by a CR1225 lithium battery, will beep for days and, best of all, it's programmable, featuring an ATtiny25 micro-controller. This will be perfect! 

Dr Evil

Now all I need to do is revise the original firmware.

The beeper is designed to do two main things: provide a configurable flight time alarm, and provide an audible beacon to help you find your plane if you lose control and can't see where it landed. It also tells you if your CR1225 is getting low.

While I don't need any of that code, the beeping and delay functions made it a snap to reprogram the little guy to sound like a cricket.

The main loop, once simplified, looks like the following.

 
int main() {
disableWatchdog();
sei();

slowClock();

loop();
}
 

To save battery, the module powers down and only wakes up when the watchdog timer fires. It also slows down the main clock using the clock pre-scaler function and the CLKDIV8 fuse. Just to be sure the watchdog timer doesn't fire too soon, it is disabled until we're ready to loop. Interrupts are enabled with sei(). Then the clock is slowed down, then it begins the main loop, shown below.


void loop() {
uint16_t next_chirp_sec = 0;
int r = 0;

while (1) {
enableWatchdog(); // (re-)enable watchdog
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_mode();

if (after(next_chirp_sec)) {
chirp(CHIRP_CYCLES);

next_chirp_sec += 5 * random[r];
r += 1;
if (r >= 16) r = 0;
}
}
}

The main loop function is responsible for checking to see if it's time to chirp, chirping, and managing the sleep/wake cycle. 

The MCU runs the loop once every second. To do so it first enables the watchdog timer (WDT). It has to do this in each pass of the loop because after the WDT interrupt fires and wakes up the MCU, it is automatically disabled by hardware. That done, the code puts the tiny into power down mode to be awoken later.

After a second, the WDT wakes up the MCU and it checks if it is time to chirp. If so, it chirps and sets the time for the next chirp. In either case, it starts the loop over, and goes to sleep, and so on.

The interval between chirps is made maddeningly erratic by selecting the next value in the random array. The index starts over at 0 when it reaches the end of the array. Hopefully the interval is sufficient to make this somewhat difficult to find but not too difficult.

 
static long random[16] = {
8, 7, 6, 26, 4, 8, 5, 11, 17, 11, 2, 18, 4, 10, 19, 30
};

Finally, the chirp is simply a series of short beeps, each separated by a short interval.

void chirp(int l) {
int i;
for (i = 0; i < l; i++) {
beepOn();
wait_ms(CHIRP_DELAY_MS);
beepOff();
wait_ms(CHIRP_DELAY_MS);
}
}

By listening to a couple of cricket videos I determined by ear that a chirp delay of 12ms and 13 cycles was close enough to real life.

Update: Annoying cricket was annoying. Success! I had to give in and show her where it was before it drove the poor girl crazy.

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.