Simple Stop Watch using Interrupts
Contents
Simple Stop Watch using Interrupts#
Particle CLI#
Installation#
%%capture
!bash <( curl -sL https://particle.io/install-cli )
# path to the particle cli. May be environment dependent.
particle_cli = "/root/bin/particle"
Utility functions#
import re
import subprocess
# regular expression to strip ansi control characters
ansi = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])')
# decode byte string and strip ansi control characters
def decode_bytes(byte_string):
if isinstance(byte_string, bytes):
result = byte_string.decode("utf-8")
return ansi.sub("", result)
# streamline call to the particle-cli
def particle(args):
process = subprocess.run(["/root/bin/particle"] + args,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
process.stdout = decode_bytes(process.stdout)
process.stderr = decode_bytes(process.stderr)
return process
Login to Particle#
import getpass
# prompt for username and password
username = getpass.getpass(prompt="Username: ")
password = getpass.getpass(prompt="Password: ")
# attempt login
output = particle(["login", "--username", username, "--password", password])
# report results
if output.returncode:
print(f"Return code = {output.returncode}")
print(output.stderr)
else:
print(output.stdout)
Username: ··········
Password: ··········
> Successfully completed login!
Select a device#
The following cell downloads a list of all user devices and creates a list of device names. Here we choose the first name in the list for the rest of this notebook. If this is not the device to be used, then modify this cell accordingly.
devices = [line.split()[0] for line in particle(["list"]).stdout.splitlines()]
device_name = devices[0]
print(particle(["list", device_name]).stdout)
jck_argon_01 [e00fce68eaceb1faa7cf7193] (Argon) is online
Project: Simple Stop Watch#
The goal of this project is to build a simple stop watch. The project will use code previously developed for the Grove 4 digit display, and add a Grove button to control operation of the stop watch. The stop watch will start and stop with a short click of the button, and reset to zero with a long button press.
Solution 2: Interrupt Service Routine (ISR)#
The clickButton
library provides an easy-to-use method of managing the button actions, with provisions for debouncing, multiple clicks, and long clicks, but testing shows the button updates when the button is released, not when it is pressed. This is not consistent with a user’s expectation that the clock should stop and start on the press of the button, not on the release.
The following cell demonstrates the use of an Interrupt Service Routine to manage the button interface. The key insight here is to respond to both the press and release of the button by specifying CHANGE
in the attachInterrupt
function. This makes is possible to detect a long button press to reset the stop watch display to zero.
%%writefile src/myproject.ino
/* pin assignments */
#define PIN_CLK D2 /* display clock */
#define PIN_DIO D3 /* display data */
#define PIN_BTN D4 /* button */
/* display parameters */
#define DIGITS 4 /* display digits */
#include "Grove_4Digit_Display.h"
#include "clickButton.h"
/* display object */
TM1637 tm1637(PIN_CLK, PIN_DIO);
/* stopwatch state */
unsigned long curr_time;
unsigned long prev_time;
unsigned long display_time;
volatile unsigned long btn_press_time;
volatile bool btn_is_pressed;
volatile bool running;
void setup() {
/* setup display */
tm1637.init();
tm1637.set(BRIGHT_TYPICAL);
tm1637.point(POINT_ON);
/* setup button */
pinMode(PIN_BTN, INPUT);
btn_press_time = millis();
attachInterrupt(PIN_BTN, on_btn_change, CHANGE);
/* setup stopwatch */
prev_time = millis();
display_time = 0;
running = FALSE;
}
void loop() {
curr_time = millis();
if (running) {
display_time += curr_time - prev_time;
if (btn_is_pressed && ((curr_time - btn_press_time) > 1000)) {
running = FALSE;
display_time = 0;
}
}
prev_time = curr_time;
display(display_time / 10); /* displaying 100th's of seconds */
}
void on_btn_change() {
if (digitalRead(PIN_BTN)==HIGH) {
if ((millis() - btn_press_time) > 50) {
running = !running;
btn_press_time = millis();
btn_is_pressed = TRUE;
}
} else {
btn_is_pressed = FALSE;
}
}
void display(unsigned int number) {
for (int i = 0; i < 4; i++) {
int digit = DIGITS - 1 - i;
tm1637.display(digit, number % 10);
number /= 10;
}
}
Overwriting src/myproject.ino
print(particle(["compile", "argon", "--saveTo", "myproject.bin"]).stdout)
Compiling code for argon
Including:
src/myproject.ino
project.properties
attempting to compile firmware
downloading binary from: /v1/binaries/5f91c394d59546684d4c092a
saving to: myproject.bin
Memory use:
text data bss dec hex filename
6604 108 1088 7800 1e78 /workspace/target/workspace.elf
Compile succeeded.
Saved firmware to: /content/myproject/myproject.bin
print(particle(["flash", device_name, "myproject.bin"]).stdout)
Including:
myproject.bin
attempting to flash firmware to your device jck_argon_01
Flash device OK: Update started
Flash success!