Skip to content

Commit

Permalink
feat: Add contactor
Browse files Browse the repository at this point in the history
  • Loading branch information
arcadien committed Nov 15, 2023
1 parent 1d623b1 commit 39ed25d
Show file tree
Hide file tree
Showing 7 changed files with 140 additions and 74 deletions.
26 changes: 14 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Set of applications targetting Atmel/Microchip [ATMega328P](https://www.microchip.com/en-us/product/atmega328p), implementing shooting games.

Using a *gun*, users tries to hit *targets* managed by a *target host*.
Using a _gun_, users tries to hit _targets_ managed by a _target host_.
The game is up to four players: yellow, green, red and blue.
There are 5 targets.

Expand All @@ -11,36 +11,38 @@ There are 5 targets.
Various games will be implemented.

#### Simple shooter

Each player shoot until the five targets are hit. Number of shoots is counted on the gun. Successful hits are counted by the target host and sent to the remote application.

## Functional details

### target

The **target host** application manages player points and turns. It communicates with an Android application implemented on top of [Bluetooth Electronics](https://www.keuwl.com/apps/bluetoothelectronics/) application. It manages the game logic and player points. Various games and modes can be implemented for the host. Each of these could have a specific remote UI.


### Gun

The **Gun** application manages the gun. Using two buttons, it allows:

| Press | Button 1 | Button 2 |
|-------|------------------|--------------------------------|
| short | shoot | reset shoot count |
| long | N/A | continuous laser (calibration) |
| Press | Button 1 | Button 2 |
| ----- | -------- | ------------------------------ |
| short | shoot | reset shoot count |
| long | N/A | continuous laser (calibration) |

Gun application manages following outputs:
* Laser
* vibrator
* 128x32 I²C display

- Laser
- vibrator
- 128x32 I²C display

## Architecture details

Some parts of the projet, implementing logic, can be used on both native and target platform.
All that code is represented in the "domain" package below.

Both **target** and **Gun** application are run both native and cross environment. The current
target hardware for both is the [ATMega328p](https://www.microchip.com/en-us/product/atmega328p) chip from Atmel/Microchip.
Current target hardware for all applications is the [ATMega328p](https://www.microchip.com/en-us/product/atmega328p) chip from Atmel/Microchip.
To facilitate testing and extension, user interface (Gui) and hardware abstraction (HAL) are provided using interfaces. Implementation exists for native and cross (i.e. testing) environments.

Note: The *BTEGui* implementation can be used in native environment. Based on plain string exchange, if it may be difficult to interpret.
Note: The _BTEGui_ implementation can be used in native environment. Based on plain string exchange, if it may be difficult to interpret.

![Architecture overview](architecture.png)
3 changes: 2 additions & 1 deletion lib/Domain/Contactor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,12 @@ void Contactor::onDown(long now) { downStartTime = now; }
void Contactor::onUp(long now) {
onShortPress();
checkForLongPress(now);
downStartTime = 0;
}

void Contactor::checkForLongPress(long now) {
if ((downStartTime > 0) && ((now - downStartTime) > LONG_PRESS)) {
downStartTime = 0;
onLongPress();
downStartTime = 0;
}
}
12 changes: 6 additions & 6 deletions lib/Domain/Contactor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ class Contactor {
volatile long downStartTime = 0;

public:
void checkForLongPress(long now);
void onDown(long now);
void onUp(long now);
virtual void checkForLongPress(long now);
virtual void onDown(long now);
virtual void onUp(long now);

virtual ~Contactor() {}
virtual void onShortPress() = 0;
virtual void onLongPress() = 0;

virtual void onShortPress(){};
virtual void onLongPress(){};
};
2 changes: 1 addition & 1 deletion lib/Domain/Gun.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class Gun {

public:
uint8_t availableShots;
Gun(IGunHal &hal) : _hal(hal), availableShots(0) {}
Gun(IGunHal &hal) : _hal(hal) {}
void onButton1ShortPress();
void onButton1LongPress();
void onButton2ShortPress();
Expand Down
4 changes: 2 additions & 2 deletions lib/Domain/IGunHal.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
class IGunHal {
public:
virtual ~IGunHal() {}
virtual bool isButton1Pressed() = 0;
virtual bool isButton2Pressed() = 0;
virtual bool isTriggerDown() = 0;
virtual bool isButtonDown() = 0;
virtual void shortDelay() = 0;
virtual void longDelay() = 0;
virtual void ledOn() = 0;
Expand Down
10 changes: 5 additions & 5 deletions platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ lib_deps =
# -cavrisp2
# -cdragon_isp
# -cavrispmkII
upload_command = avrdude -vv -b57600 -pm328p -c avrisp -Pcom10 $UPLOAD_FLAGS -U flash:w:$SOURCE:i
upload_command = avrdude -vv -b57600 -pm328p -c avrisp -Pcom9 $UPLOAD_FLAGS -U flash:w:$SOURCE:i

# upload_protocol=custom
# upload_speed=115200
Expand All @@ -39,18 +39,18 @@ upload_command = avrdude -vv -b57600 -pm328p -c avrisp -Pcom10 $UPLOAD_FLAGS -U
platform=native
build_type = release
test_ignore = cross/*
build_flags = -DNATIVE
debug_test = noarch/test_gun
build_flags = -DNATIVE -O0
debug_test = noarch/test_contactor
build_src_filter = ${env.src_filter} -<TargetApp.cpp> -<GunApp.cpp>

[env:native_debug]
lib_deps = ${env.lib_deps}
fakeit=https://github.com/FabioBatSilva/ArduinoFake.git
platform=native
build_type = debug
debug_build_flags = -Og -ggdb3 -g3 -DNATIVE -UAVR
debug_build_flags = -Og -ggdb3 -g3 -DNATIVE
test_ignore = cross/*
debug_test = noarch/test_gun
debug_test = noarch/test_contactor
build_src_filter = ${env.src_filter} -<TargetApp.cpp> -<GunApp.cpp>

[env:cross]
Expand Down
157 changes: 110 additions & 47 deletions src/GunApp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
#include <Fonts/FreeMonoBold18pt7b.h>
#include <Fonts/FreeMonoOblique9pt7b.h>

#include <Contactor.hpp>

#include <LowPower.h>

#define TRIGGER_PIN 2
Expand All @@ -36,8 +38,9 @@
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 32 // OLED display height, in pixels
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS \
0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
// 0x3D for 128x64, 0x3C for 128x32
#define SCREEN_ADDRESS 0x3C

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

volatile uint16_t hitcount;
Expand All @@ -48,47 +51,107 @@ volatile uint16_t shootCount;
#define F_CPU 16000000L
#define TICKS_BETWEEN_UI_UPDATE 1000 // 10 sec

// counter for ticks while firing
volatile uint8_t shootCycleCountdown;
// display API
static void displayBatteryStatus();
static void displayShootCount();

void setup();
static void setupHeartbeat();

// counter for ticks between battery display update
volatile uint16_t updateBatteryDisplayCycleCount;

void shoot() {
// wake up!
if (shootCycleCountdown == 0) {
shootCount += 1;
digitalWrite(LASER_PIN, HIGH);
digitalWrite(OUT2_PIN, HIGH);
shootCycleCountdown = SHOOT_DURATION_CYCLE;
class Trigger : public Contactor {
public:
Trigger() : shootCycleCountdown(0) {}

// counter for ticks while firing
volatile uint8_t shootCycleCountdown;

void decreaseCycleCount() {
if (shootCycleCountdown > 0) {
shootCycleCountdown--;
}
}
}
void reload() {
// wake up!
shootCount = 0;
}

void setupHeartbeat() {
void onDown(long now) override {

// set Timer2 in CTC mode, 1/1024 prescaler, start the timer ticking...
TCCR2A = (1U << WGM21) | (0U << WGM20);
TCCR2B = (1U << CS22) | (1U << CS21) | (1U << CS20); // 1/2^10
ASSR &= ~(1U << AS2);
TIMSK2 = (1U << OCIE2A); // enable TIMER2 compare Interrupt
TCNT2 = 0U;
Contactor::onDown(now);

// set the output-compare register based on the desired tick frequency
OCR2A = (F_CPU / BSP_TICKS_PER_SEC / 1024U) - 1U;
// do not wait trigger released to shoot

if (shootCycleCountdown == 0) {
shootCount += 1;
digitalWrite(LASER_PIN, HIGH);
digitalWrite(OUT2_PIN, HIGH);
shootCycleCountdown = SHOOT_DURATION_CYCLE;
}
}
};

bool calibrationMode;

class Button : public Contactor {
public:
void onShortPress() override { shootCount = 0; }
void onLongPress() override { calibrationMode = !calibrationMode; }
};

Trigger trigger;
Button button;

void triggerInterruptHandler() {
long now = millis();
if (bit_is_set(PIND, PD2)) {
trigger.onUp(now);
} else {
trigger.onDown(now);
}
}

void buttonInterruptHandler() {
long now = millis();
if (bit_is_set(PIND, PD3)) {
button.onUp(now);
} else {
button.onDown(now);
}
}

ISR(TIMER2_COMPA_vect) {
if (shootCycleCountdown > 0) {
shootCycleCountdown--;
} else if (updateBatteryDisplayCycleCount > 0) {
trigger.decreaseCycleCount();
if (updateBatteryDisplayCycleCount > 0) {
updateBatteryDisplayCycleCount--;
}
}

void loop(void) {

long now = millis();

button.checkForLongPress(now);

if (calibrationMode) {
digitalWrite(LASER_PIN, HIGH);
digitalWrite(OUT2_PIN, LOW);

} else {

if (trigger.shootCycleCountdown == 0) {
digitalWrite(LASER_PIN, LOW);
digitalWrite(OUT2_PIN, LOW);
displayShootCount();
}

if (updateBatteryDisplayCycleCount == 0) {
displayBatteryStatus();
}
}

LowPower.idle(SLEEP_FOREVER, ADC_OFF, TIMER2_ON, TIMER1_OFF, TIMER0_OFF,
SPI_OFF, USART0_OFF, TWI_OFF);
}

static void displayShootCount() {
display.fillRect(11, 0, 68, 30, 0); // clear

Expand Down Expand Up @@ -146,6 +209,19 @@ static void displayBatteryStatus() {
display.setFont(&FreeMonoBold18pt7b);
}

static void setupHeartbeat() {

// set Timer2 in CTC mode, 1/1024 prescaler, start the timer ticking...
TCCR2A = (1U << WGM21) | (0U << WGM20);
TCCR2B = (1U << CS22) | (1U << CS21) | (1U << CS20); // 1/2^10
ASSR &= ~(1U << AS2);
TIMSK2 = (1U << OCIE2A); // enable TIMER2 compare Interrupt
TCNT2 = 0U;

// set the output-compare register based on the desired tick frequency
OCR2A = (F_CPU / BSP_TICKS_PER_SEC / 1024U) - 1U;
}

void setup(void) {

#if defined(SERIAL_OUTPUT)
Expand All @@ -160,8 +236,9 @@ void setup(void) {
for (;;)
; // Don't proceed, loop forever
}

shootCount = 0;
shootCycleCountdown = 0;
calibrationMode = false;
updateBatteryDisplayCycleCount = TICKS_BETWEEN_UI_UPDATE;

pinMode(LASER_PIN, OUTPUT);
Expand Down Expand Up @@ -197,27 +274,13 @@ void setup(void) {
displayShootCount();

pinMode(BUTTON2_PIN, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(BUTTON2_PIN), reload, FALLING);
attachInterrupt(digitalPinToInterrupt(BUTTON2_PIN), buttonInterruptHandler,
CHANGE);

pinMode(TRIGGER_PIN, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(TRIGGER_PIN), shoot, FALLING);
attachInterrupt(digitalPinToInterrupt(TRIGGER_PIN), triggerInterruptHandler,
CHANGE);
cli();
setupHeartbeat();
sei();
}

void loop(void) {

if (shootCycleCountdown == 0) {

digitalWrite(LASER_PIN, LOW);
digitalWrite(OUT2_PIN, LOW);
displayShootCount();

if (updateBatteryDisplayCycleCount == 0) {
displayBatteryStatus();
}
}
LowPower.idle(SLEEP_FOREVER, ADC_OFF, TIMER2_ON, TIMER1_OFF, TIMER0_OFF,
SPI_OFF, USART0_OFF, TWI_OFF);
}

0 comments on commit 39ed25d

Please sign in to comment.