Compare commits
20 Commits
Author | SHA1 | Date | |
---|---|---|---|
940fa5b9b1 | |||
09dfd6c6f5 | |||
a78d45e94b | |||
284065b62f | |||
ac9eb9bac9 | |||
4f91856445 | |||
26382c9712 | |||
367ff5d8b5 | |||
eff94f9841 | |||
3ce0eaea91 | |||
660d351bcb | |||
972f768512 | |||
ad72c18556 | |||
af8aca9218 | |||
7d425f6cfb | |||
aaeed65c18 | |||
4229353d93 | |||
b83ede23ef | |||
ebbea376a6 | |||
fdef34e663 |
207
README.md
207
README.md
@ -1,4 +1,4 @@
|
||||
This firmware template is designed for development boards based around [STM32 F1 series micro-controller](http://www.st.com/web/en/catalog/mmc/FM141/SC1169/SS1031).
|
||||
The dachtür is a device to restrict access to the dachboden.
|
||||
|
||||
project
|
||||
=======
|
||||
@ -6,51 +6,161 @@ project
|
||||
summary
|
||||
-------
|
||||
|
||||
*describe project purpose*
|
||||
Previously a button allowed to open the building's entrance door and granted access to the dachboden.
|
||||
Since the dachboden got popular, it is now flooded.
|
||||
The idea behind the dachtür it to disable this button and replace it with a secret sequence of button presses during certain periods (e.g. when parties are taking place in the dachboden).
|
||||
It must be installed in the door panel, as described under `connections`.
|
||||
|
||||
technology
|
||||
----------
|
||||
configure
|
||||
---------
|
||||
|
||||
*described electronic details*
|
||||
To program the days, times, and button sequence, connect to the dachtür.
|
||||
It should appear as "dachtuer" Bluetooth (classic) device.
|
||||
A PIN is required to connect to it.
|
||||
Once connected, you can use a terminal (such as the "Bluetooth terminal" android application).
|
||||
Interact with it by entering commands.
|
||||
- from time to time, update the date (it does not take into account summer and winter time):
|
||||
~~~
|
||||
date YYYY-MM-DD HH:MM
|
||||
~~~
|
||||
- enter the days on which the access should be restricted (starting with Monday, here only Thursday is active):
|
||||
~~~
|
||||
days 0001000
|
||||
~~~
|
||||
- enter at which time the restriction should start:
|
||||
~~~
|
||||
start 20:00
|
||||
~~~
|
||||
- enter at which time the restriction should stop:
|
||||
~~~
|
||||
stop 06:00
|
||||
~~~
|
||||
- enter the secret button sequence to open the door (here press button 1 then 2):
|
||||
~~~
|
||||
password 12
|
||||
~~~
|
||||
|
||||
board
|
||||
=====
|
||||
|
||||
The current implementation uses a [core board](https://wiki.cuvoodoo.info/doku.php?id=stm32f1xx#core_board).
|
||||
|
||||
The underlying template also supports following board:
|
||||
|
||||
- [Maple Mini](http://leaflabs.com/docs/hardware/maple-mini.html), based on a STM32F103CBT6
|
||||
- [System Board](https://wiki.cuvoodoo.info/doku.php?id=stm32f1xx#system_board), based on a STM32F103C8T6
|
||||
- [blue pill](https://wiki.cuvoodoo.info/doku.php?id=stm32f1xx#blue_pill), based on a STM32F103C8T6
|
||||
- [black pill](https://wiki.cuvoodoo.info/doku.php?id=stm32f1xx#black_pill), based on a STM32F103C8T6
|
||||
- [core board](https://wiki.cuvoodoo.info/doku.php?id=stm32f1xx#core_board), based on a STM32F103C8T6
|
||||
- [ST-LINK V2 mini](https://wiki.cuvoodoo.info/doku.php?id=jtag#mini_st-link_v2), a ST-LINK/V2 clone based on a STM32F101C8T6
|
||||
- [USB-Blaster](https://wiki.cuvoodoo.info/doku.php?id=jtag#armjishu_usb-blaster), an Altera USB-Blaster clone based on a STM32F101C8T6
|
||||
|
||||
**Which board is used is defined in the Makefile**.
|
||||
This is required to map the user LED and button provided on the board
|
||||
|
||||
The ST-LINK V2 mini clone has SWD test points on the board.
|
||||
Because read protection is enabled, you will first need to remove the protection to be able to flash the firmware.
|
||||
To remove the read protection (and erase flash), run `rake remove_protection` while a SWD adapter is connected.
|
||||
|
||||
The Altera USB-Blaster clone has a pin header for SWD and UART1 on the board.
|
||||
SWD is disabled in the main firmware, and it has read protection.
|
||||
To be able to flash using SWD (or the serial port), the BOOT0 pin must be set to 1 to boot the system memory install of the flash memory.
|
||||
To set BOOT0 to 1, apply 3.3 V on R11, between the resistor and the reference designator, when powering the device.
|
||||
The red LED should stay off while the green LED is on.
|
||||
Now you can remove the read protection (and erase flash), run `rake remove_protection` while a SWD adapter is connected.
|
||||
WARNING: If the internal backup battery gets empty and the device looses power, the configuration and date get lost (since there are stored in SRAM).
|
||||
In this case, replace the battery and reconfigure the device.
|
||||
|
||||
connections
|
||||
===========
|
||||
|
||||
Connect the peripherals the following way (STM32F10X signal; STM32F10X pin; peripheral pin; peripheral signal; comment):
|
||||
On one side there are three XH-2.54 connectors.
|
||||
|
||||
- *list board to peripheral pin connections*
|
||||
2-pin connector: 6-25 AC/DC power input
|
||||
|
||||
3-pin connector: opening button (the one already wired)
|
||||
- 1 (left-most): common side of button (leave the bus bar connected)
|
||||
- 2 (center): other side of the button (remove the cable)
|
||||
- 3 (right-most): the cable which was connected to the button
|
||||
|
||||
4-pin connector: ring buttons (yet unused)
|
||||
- 2 left-most pins: to button 1 of door panel
|
||||
- 2 right-most pins: to button 2 of door panel
|
||||
|
||||
On the other side is a USB connector.
|
||||
This is used to flash and configure the micro-controller.
|
||||
|
||||
peripherals
|
||||
===========
|
||||
|
||||
The XH-2.54 2P connector is for the 15V AC power input.
|
||||
A full bridge rectifier and smoothing capacitor make a DC power supply out of it.
|
||||
A buck converter module steps it down to 5V.
|
||||
Under 50 mA of power consumption it is very inefficient.
|
||||
With no load it has a quiescent current of 37 mA.
|
||||
The 5V rail provides power to the blue pill, Bluetooth module, and boost converter.
|
||||
|
||||
The MT3608-based boost converter steps the voltage up to 9V for the omrom G6E-134P relays.
|
||||
It is more efficient to re-use the existing 5V than stepping down the 15V because the buck converters drawn a large current under no load.
|
||||
|
||||
The XH-2.54 3P is to be connected to door opening button (the installer will know what I mean).
|
||||
Two omrom G6E-134P relays will take control over the button.
|
||||
This allows the door button to be used.
|
||||
At the specified time intervals, this is disconnected.
|
||||
When the right code is entered, the second relay simulates the button press and opens the door.
|
||||
The relays are controlled by the board using two 2N7000 n-channel MOSFETs.
|
||||
|
||||
The XH-2.54 4P is to be connected with the two buttons.
|
||||
They are used to enter the secret sequence to open the door.
|
||||
|
||||
The [blue pill](https://wiki.cuvoodoo.info/doku.php?id=stm32f1xx#blue_pill), based on a STM32F103C8T6 micro-controller, will handle the logic.
|
||||
|
||||
A CR2032 battery is connected through a diode to VBAT.
|
||||
This keeps the real time clock (RTC) running when there is no power from the panel.
|
||||
A 3.3V pin is also connected through a diode to VBAT.
|
||||
This powered the RTC when the board is powered, saving the battery.
|
||||
|
||||
The HC-05 Bluetooth module allows to remotely connect to the UART port.
|
||||
It has been configured (using `hc-05_porg.rb`) to appear as "dachtuer", and requires a PIN to pair.
|
||||
It offers the Serial Port Profile (SPP) from Bluetooth classic (Bluetooth Low Energy makes little sense).
|
||||
This allows to communicate with the board without having to remove the panel.
|
||||
It can be used to configure the opening time slots, days, and button sequence.
|
||||
It is also possible to update the firmware through it.
|
||||
|
||||
A WS2812B LED strip is also connected to the board.
|
||||
The LEDs should be placed behind the button name shield.
|
||||
An animation with be shown when pressing the button (only during opening hours).
|
||||
|
||||
wiring
|
||||
======
|
||||
|
||||
HC-05 Bluetooth module:
|
||||
- STATE: no connect
|
||||
- TXD: PA10/USART1_RX
|
||||
- RXD: PA9/USART1_TX
|
||||
- GND: ground
|
||||
- VCC: 5V
|
||||
- EN: no connect
|
||||
|
||||
CR2032 coin cell battery:
|
||||
- +: VBAT, though diode (VBAT should also be connected to 3.3V through diode)
|
||||
- -: ground
|
||||
|
||||
LED (because the on-board LED screws the LSE):
|
||||
- anode: 3.3V, though resistor (1K)
|
||||
- cathode: PB11
|
||||
|
||||
omrom G6E-134P relay (bottom, for button):
|
||||
- 1 +: 9V
|
||||
- 6 -: 2N7000 drain
|
||||
- 7 COM: door button bar
|
||||
- 10 NC: no connect
|
||||
- 12 NO: other relay NO
|
||||
|
||||
2N7000 p-channel MOSFET (for bottom relay):
|
||||
- 1 source: ground
|
||||
- 2 gate: PB6 (pulled low)
|
||||
- 3 drain: relay -
|
||||
|
||||
omrom G6E-134P relay (top, for panel):
|
||||
- 1 +: 9V
|
||||
- 6 -: 2N7000 drain
|
||||
- 7 COM: wire to panel
|
||||
- 10 NC: door button (where the wire was)
|
||||
- 12 NO: other relay NO
|
||||
|
||||
2N7000 p-channel MOSFET (for top relay):
|
||||
- 1 source: ground
|
||||
- 2 gate: PB7 (pulled low)
|
||||
- 3 drain: relay -
|
||||
|
||||
WS2812B RGB LED strip:
|
||||
- VCC: 5V
|
||||
- DIN: PB15
|
||||
- GND: ground
|
||||
|
||||
XH-4P:
|
||||
- 1: ground (for button 1)
|
||||
- 2: PB8 (for button 1)
|
||||
- 3: ground (for button 2)
|
||||
- 4: PB9 (for button 2)
|
||||
|
||||
All pins are configured using `define`s in the corresponding source code.
|
||||
|
||||
The prototype uses a SZOMK AK-N-01 enclosures salvaged from a J-Link clone.
|
||||
|
||||
code
|
||||
====
|
||||
|
||||
@ -81,16 +191,35 @@ The `bootloader` is started first and immediately jumps to the `application` if
|
||||
The `application` image is the main application and is implemented in `application.c`.
|
||||
It is up to the application to advertise USB DFU support (i.e. as does the provided USB CDC ACM example).
|
||||
|
||||
The `bootloader` image will be flashed using SWD (Serial Wire Debug).
|
||||
The `bootlaoder` image will be flashed using SWD (Serial Wire Debug).
|
||||
For that you need an SWD adapter.
|
||||
The `Makefile` uses a ST-Link V2 programmer along OpenOCD software (default), or Black Magic Probe.
|
||||
The `Makefile` uses a ST-Link V2 along OpenOCD software.
|
||||
To flash the `booltoader` using SWD run `rake flash_booloader`.
|
||||
If the development board uses the CKS32 chip STM32 alternative, use `CPUTAPID=0x2ba01477 rake flash_booloader`.
|
||||
|
||||
Once the `bootloader` is flashed it is possible to flash the `application` over USB using the DFU protocol by running `rake flash`.
|
||||
To force the bootloader to start the DFU mode press the user button or short a pin, depending on the board.
|
||||
It is also possible to flash the `application` image using SWD by running `rake flash_application`.
|
||||
|
||||
It is also possible to flash the application over Bluetooth as follows:
|
||||
~~~
|
||||
# start bluetooth
|
||||
sudo systemctl restart bluetooth
|
||||
# pair device (you only need to do it once, and you need the PIN)
|
||||
bluetoothctl
|
||||
power on
|
||||
pair 20:15:02:02:16:28
|
||||
quit
|
||||
# connect to device
|
||||
sudo rfcomm bind rfcomm0 20:15:02:02:16:28
|
||||
# switch to bootloader
|
||||
echo "embedded" > /dev/rfcomm0
|
||||
# flash firmware (it always fails the first time)
|
||||
stm32flash /dev/rfcomm0
|
||||
stm32flash /dev/rfcomm0 -b 115200 -S 0x08002000 -w application.bin -R
|
||||
# disconnect from device
|
||||
sudo rfcomm release rfcomm0
|
||||
~~~
|
||||
|
||||
debug
|
||||
-----
|
||||
|
||||
@ -100,4 +229,4 @@ To start the debugging session run `rake debug`.
|
||||
USB
|
||||
---
|
||||
|
||||
The firmware offers serial communication over USART1 and USB (using the CDC ACM device class).
|
||||
The firmware offers serial communication over USART1 (where the Bluetooth module is connected) and USB (using the CDC ACM device class).
|
||||
|
12
Rakefile
12
Rakefile
@ -193,8 +193,6 @@ OOCD_INTERFACE = ENV["OOCD_INTERFACE"] || (SWD_ADAPTER=="STLINKV2" ? "stlink" :
|
||||
OOCD_TARGET = "stm32f1x"
|
||||
# Black Magic Probe port
|
||||
BMP_PORT = ENV["BMP_PORT"] || "/dev/ttyACM0"
|
||||
# set CPUTAPID (0x1ba01477 for STM32, 0x2ba01477 for CKS32/APM32)
|
||||
CPUTAPID = ENV["CPUTAPID"] || "0x1ba01477"
|
||||
|
||||
desc "flash application using USB DFU"
|
||||
task :flash => APPLICATION+".bin" do |t|
|
||||
@ -205,7 +203,7 @@ desc "remove STM32F1 protection using SWD"
|
||||
task :remove_protection do
|
||||
case SWD_ADAPTER
|
||||
when "STLINKV2"
|
||||
sh "#{OOCD} --file interface/#{OOCD_INTERFACE}.cfg --command 'transport select hla_swd' --command 'set CPUTAPID #{CPUTAPID}' --file target/#{OOCD_TARGET}.cfg --command 'init' --command 'halt' --command 'reset init' --command 'stm32f1x unlock 0' --command 'reset init' --command 'flash protect 0 0 last off' --command 'reset init' --command 'stm32f1x options_write 0 SWWDG NORSTSTNDBY NORSTSTOP' --command 'reset init' --command 'stm32f1x mass_erase 0' --command 'shutdown'"
|
||||
sh "#{OOCD} --file interface/#{OOCD_INTERFACE}.cfg --file target/#{OOCD_TARGET}.cfg --command 'init' --command 'reset init' --command 'stm32f1x unlock 0' --command 'reset init' --command 'flash protect 0 0 last off' --command 'reset init' --command 'stm32f1x options_write 0 SWWDG NORSTSTNDBY NORSTSTOP' --command 'reset init' --command 'stm32f1x mass_erase 0' --command 'shutdown'"
|
||||
when "BMP"
|
||||
sh "#{GDB} --eval-command='target extended-remote #{BMP_PORT}' --eval-command='set confirm off' --eval-command='monitor swdp_scan' --eval-command='attach 1' --eval-command='monitor option erase' --eval-command='monitor erase_mass' --eval-command='kill' --eval-command='quit'"
|
||||
end
|
||||
@ -215,7 +213,7 @@ desc "flash bootloader using SWD"
|
||||
task :flash_bootloader => BOOTLOADER+".hex" do |t|
|
||||
case SWD_ADAPTER
|
||||
when "STLINKV2"
|
||||
sh "#{OOCD} --file interface/#{OOCD_INTERFACE}.cfg --command 'transport select hla_swd' --command 'set CPUTAPID #{CPUTAPID}' --file target/#{OOCD_TARGET}.cfg --command 'init' --command 'halt' --command 'reset init' --command 'flash erase_sector 0 0 last' --command 'flash write_image erase #{t.source}' --command 'reset' --command 'shutdown'"
|
||||
sh "#{OOCD} --file interface/#{OOCD_INTERFACE}.cfg --file target/#{OOCD_TARGET}.cfg --command 'init' --command 'reset init' --command 'flash erase_sector 0 0 last' --command 'flash write_image erase #{t.source}' --command 'reset' --command 'shutdown'"
|
||||
when "BMP"
|
||||
sh "#{GDB} --eval-command='target extended-remote #{BMP_PORT}' --eval-command='set confirm off' --eval-command='monitor swdp_scan' --eval-command='attach 1' --eval-command='monitor erase_mass' --eval-command='load' --eval-command='kill' --eval-command='quit' #{t.source}"
|
||||
end
|
||||
@ -225,7 +223,7 @@ desc "flash application using SWD"
|
||||
task :flash_application => APPLICATION+".hex" do |t|
|
||||
case SWD_ADAPTER
|
||||
when "STLINKV2"
|
||||
sh "#{OOCD} --file interface/#{OOCD_INTERFACE}.cfg --command 'transport select hla_swd' --command 'set CPUTAPID #{CPUTAPID}' --file target/#{OOCD_TARGET}.cfg --command 'adapter speed 100' --command 'init' --command 'halt' --command 'reset init' --command 'flash write_image erase #{t.source}' --command 'reset' --command 'shutdown'"
|
||||
sh "#{OOCD} --file interface/#{OOCD_INTERFACE}.cfg --file target/#{OOCD_TARGET}.cfg --command 'init' --command 'reset init' --command 'flash write_image erase #{t.source}' --command 'reset' --command 'shutdown'"
|
||||
when "BMP"
|
||||
sh "#{GDB} --eval-command='target extended-remote #{BMP_PORT}' --eval-command='set confirm off' --eval-command='monitor swdp_scan' --eval-command='attach 1' --eval-command='load' --eval-command='kill' --eval-command='quit' #{t.source}"
|
||||
end
|
||||
@ -237,7 +235,7 @@ task :debug => APPLICATION+".elf" do |t|
|
||||
case SWD_ADAPTER
|
||||
when "STLINKV2"
|
||||
# for GDB to work with openOCD the firmware needs to be reloaded
|
||||
exec("#{GDB} --eval-command='target remote | #{OOCD} --file interface/#{OOCD_INTERFACE}.cfg --command \"transport select hla_swd\" --command \"set CPUTAPID #{CPUTAPID}\" --file target/#{OOCD_TARGET}.cfg --command \"gdb_port pipe; log_output /dev/null; init\"' #{t.source}")
|
||||
exec("#{GDB} --eval-command='target remote | #{OOCD} --file interface/#{OOCD_INTERFACE}.cfg --file target/#{OOCD_TARGET}.cfg --command \"gdb_port pipe; log_output /dev/null; init\"' --eval-command='monitor reset halt' --eval-command='load' --eval-command='monitor reset init' #{t.source}")
|
||||
when "BMP"
|
||||
exec("#{GDB} --eval-command='target extended-remote #{BMP_PORT}' --eval-command='monitor version' --eval-command='monitor swdp_scan' --eval-command='attach 1' #{t.source}")
|
||||
end
|
||||
@ -249,7 +247,7 @@ task :debug_bootloader => BOOTLOADER+".elf" do |t|
|
||||
case SWD_ADAPTER
|
||||
when "STLINKV2"
|
||||
# for GDB to work with openOCD the firmware needs to be reloaded
|
||||
exec("#{GDB} --eval-command='target remote | #{OOCD} --file interface/#{OOCD_INTERFACE}.cfg --command \"transport select hla_swd\" --command \"set CPUTAPID #{CPUTAPID}\" --file target/#{OOCD_TARGET}.cfg --command \"gdb_port pipe; log_output /dev/null; init\"' --eval-command='monitor reset init' #{t.source}")
|
||||
exec("#{GDB} --eval-command='target remote | #{OOCD} --file interface/#{OOCD_INTERFACE}.cfg --file target/#{OOCD_TARGET}.cfg --command \"gdb_port pipe; log_output /dev/null; init\"' --eval-command='monitor reset halt' --eval-command='load' --eval-command='monitor reset init' #{t.source}")
|
||||
when "BMP"
|
||||
exec("#{GDB} --eval-command='target extended-remote #{BMP_PORT}' --eval-command='monitor version' --eval-command='monitor swdp_scan' --eval-command='attach 1' #{t.source}")
|
||||
end
|
||||
|
628
application.c
628
application.c
@ -1,7 +1,20 @@
|
||||
/** STM32F1 application example
|
||||
/* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
/** dachboden front panel access control
|
||||
* @file
|
||||
* @author King Kévin <kingkevin@cuvoodoo.info>
|
||||
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
|
||||
* @date 2016-2020
|
||||
*/
|
||||
|
||||
@ -24,16 +37,17 @@
|
||||
#include <libopencm3/stm32/dbgmcu.h> // debug utilities
|
||||
#include <libopencm3/stm32/desig.h> // design utilities
|
||||
#include <libopencm3/stm32/flash.h> // flash utilities
|
||||
#include <libopencm3/stm32/f1/bkp.h> // backup domain utilities
|
||||
#include <libopencm3/stm32/timer.h> // timer utilities
|
||||
|
||||
/* own libraries */
|
||||
#include "global.h" // board definitions
|
||||
#include "print.h" // printing utilities
|
||||
#if !defined(STLINKV2)
|
||||
#include "uart.h" // USART utilities
|
||||
#endif
|
||||
#include "usb_cdcacm.h" // USB CDC ACM utilities
|
||||
#include "terminal.h" // handle the terminal interface
|
||||
#include "menu.h" // menu utilities
|
||||
#include "led_ws2812b.h" // WS2812B RGB LED control
|
||||
|
||||
/** watchdog period in ms */
|
||||
#define WATCHDOG_PERIOD 10000
|
||||
@ -41,16 +55,12 @@
|
||||
/** set to 0 if the RTC is reset when the board is powered on, only indicates the uptime
|
||||
* set to 1 if VBAT can keep the RTC running when the board is unpowered, indicating the date and time
|
||||
*/
|
||||
#if defined(CORE_BOARD)
|
||||
#define RTC_DATE_TIME 1
|
||||
#else
|
||||
#define RTC_DATE_TIME 0
|
||||
#endif
|
||||
|
||||
/** number of RTC ticks per second
|
||||
* @note use integer divider of oscillator to keep second precision
|
||||
*/
|
||||
#define RTC_TICKS_SECOND 4
|
||||
#define RTC_TICKS_SECOND 1
|
||||
|
||||
#if defined(RTC_DATE_TIME) && RTC_DATE_TIME
|
||||
/** the start time from which to RTC ticks count
|
||||
@ -65,9 +75,82 @@ static time_t time_start = 0;
|
||||
/** @defgroup main_flags flag set in interrupts to be processed in main task
|
||||
* @{
|
||||
*/
|
||||
static volatile bool rtc_internal_tick_flag = false; /**< flag set when internal RTC ticked */
|
||||
volatile bool rtc_internal_tick_flag = false; /**< flag set when internal RTC ticked */
|
||||
/** @} */
|
||||
|
||||
/** GPIO pin connected to relay, used to control button connection to panel */
|
||||
#define RELAY_PANEL_PIN PB6
|
||||
/** GPIO pin connected to relay, used to simulate button press */
|
||||
#define RELAY_BUTTON_PIN PB7
|
||||
/** GPIO for button 1 */
|
||||
#define BUTTON1_PIN PB9
|
||||
/** GPIO for button 2 */
|
||||
#define BUTTON2_PIN PB8
|
||||
|
||||
/** which button has been pressed */
|
||||
volatile uint8_t button_pressed = 0;
|
||||
|
||||
/** if we apply the opening policy */
|
||||
bool opening_apply = false;
|
||||
uint8_t pattern_length = 0;
|
||||
|
||||
static struct opening_settings_t {
|
||||
uint8_t days; /**< which days of the week it door access applies (bit 7 = Monday) */
|
||||
uint16_t start_time; /**< at which minutes of the day to start */
|
||||
uint16_t stop_time; /**< at which minutes of the day to stop */
|
||||
uint8_t button_pattern[10]; /**< sequence of buttons to press to open the door */
|
||||
} opening_settings;
|
||||
|
||||
/** timer to generate the ticks for the button LED animations */
|
||||
#define LED_ANIMATION_TIMER 2
|
||||
|
||||
/** number of timer ticks passed, for the LED animation */
|
||||
static volatile uint8_t led_animation_ticks = 0;
|
||||
|
||||
/** the button LED animation for the rust fade (duration in ticks, R, G, B) */
|
||||
static const uint8_t rust_animation[][4] = {
|
||||
{0, 0, 0, 0},
|
||||
{1, 0xb7 / 10 * 1, 0x41 / 10 * 1, 0x0e / 10 * 1},
|
||||
{1, 0xb7 / 10 * 2, 0x41 / 10 * 2, 0x0e / 10 * 2},
|
||||
{1, 0xb7 / 10 * 3, 0x41 / 10 * 3, 0x0e / 10 * 3},
|
||||
{1, 0xb7 / 10 * 4, 0x41 / 10 * 4, 0x0e / 10 * 4},
|
||||
{1, 0xb7 / 10 * 5, 0x41 / 10 * 5, 0x0e / 10 * 5},
|
||||
{1, 0xb7 / 10 * 4, 0x41 / 10 * 4, 0x0e / 10 * 4},
|
||||
{1, 0xb7 / 10 * 3, 0x41 / 10 * 3, 0x0e / 10 * 3},
|
||||
{1, 0xb7 / 10 * 2, 0x41 / 10 * 2, 0x0e / 10 * 2},
|
||||
{1, 0xb7 / 10 * 1, 0x41 / 10 * 1, 0x0e / 10 * 1},
|
||||
{0, 0, 0, 0},
|
||||
};
|
||||
|
||||
/** the button LED animation for the strobe (duration in ticks, R, G, B) */
|
||||
static const uint8_t strobe_animation[][4] = {
|
||||
{0, 0, 0, 0},
|
||||
{1, 0xff / 2, 0xff / 2, 0xff / 2},
|
||||
{2, 0, 0, 0},
|
||||
{1, 0xff / 2, 0xff / 2, 0xff / 2},
|
||||
{0, 0, 0, 0},
|
||||
};
|
||||
|
||||
/** save current opening_settings into SRAM */
|
||||
static void save_opening_settings(void)
|
||||
{
|
||||
BKP_DR1 = 0; // invalid saved settings
|
||||
BKP_DR2 = opening_settings.days & 0x7f;
|
||||
BKP_DR3 = opening_settings.start_time;
|
||||
BKP_DR4 = opening_settings.stop_time;
|
||||
BKP_DR5 = opening_settings.button_pattern[0];
|
||||
BKP_DR6 = opening_settings.button_pattern[1];
|
||||
BKP_DR7 = opening_settings.button_pattern[2];
|
||||
BKP_DR8 = opening_settings.button_pattern[3];
|
||||
BKP_DR9 = opening_settings.button_pattern[4];
|
||||
BKP_DR10 = opening_settings.button_pattern[5];
|
||||
BKP_DR11 = opening_settings.button_pattern[6];
|
||||
BKP_DR12 = opening_settings.button_pattern[7];
|
||||
BKP_DR13 = opening_settings.button_pattern[8];
|
||||
BKP_DR14 = opening_settings.button_pattern[9];
|
||||
BKP_DR1 = 0x4223; //validate saved setting
|
||||
}
|
||||
|
||||
size_t putc(char c)
|
||||
{
|
||||
size_t length = 0; // number of characters printed
|
||||
@ -120,7 +203,175 @@ static void command_reset(void* argument);
|
||||
/** switch to DFU bootloader
|
||||
* @param[in] argument no argument required
|
||||
*/
|
||||
static void command_bootloader(void* argument);
|
||||
static void command_bootloader_dfu(void* argument);
|
||||
|
||||
/** switch to system memory / embedded USART bootloader
|
||||
* @param[in] argument no argument required
|
||||
*/
|
||||
static void command_bootloader_embedded(void* argument);
|
||||
|
||||
/** show/set on which days the access policy applies
|
||||
* @param[in] argument 7x0/1 to enable day of the week, starting with Monday (optional)
|
||||
*/
|
||||
static void command_days(void* argument)
|
||||
{
|
||||
const char* days = (char*)argument; // argument is optional days
|
||||
if (NULL != argument) { // days are provided, parse and save them
|
||||
bool valid = (7 == strlen(days)); // verify input string
|
||||
for (uint8_t day = 0; day < 7 && valid; day++) {
|
||||
if (days[day] != '0' && days[day] != '1') {
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
if (valid) { // save provided settings
|
||||
// parse new days
|
||||
opening_settings.days = 0;
|
||||
for (uint8_t day = 0; day < 7; day++) {
|
||||
if ('1' == days[day]) {
|
||||
opening_settings.days |= (1 << (6 - day));
|
||||
}
|
||||
}
|
||||
save_opening_settings(); // save days
|
||||
puts("days saved\n");
|
||||
} else {
|
||||
puts("provide exactly 7 times 0 (off) or 1 (on). 1st digit for Monday, 7th digit for Sunday\n");
|
||||
}
|
||||
}
|
||||
// display current days
|
||||
printf("opening days: %07b\n", opening_settings.days);
|
||||
const char* day_names[] = {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"};
|
||||
for (uint8_t day = 0; day < LENGTH(day_names); day++) {
|
||||
printf("- %s: %s\n", day_names[day], (opening_settings.days & (1 << (6 - day))) ? "on" : "off");
|
||||
}
|
||||
}
|
||||
|
||||
/** show/set on which time the access policy starts applying
|
||||
* @param[in] argument string with time of day, optional
|
||||
*/
|
||||
static void command_start(void* argument)
|
||||
{
|
||||
const char* time = (char*)argument; // argument is optional time
|
||||
if (NULL != argument) { // days are provided, parse and save them
|
||||
bool valid = (5 == strlen(time)); // verify input string
|
||||
if (!(valid && isdigit((int8_t)time[0]) && isdigit((int8_t)time[1]) && ':' == time[2] && isdigit((int8_t)time[3]) && isdigit((int8_t)time[4]))) {
|
||||
valid = false;
|
||||
}
|
||||
if (valid) { // save provided settings
|
||||
opening_settings.start_time = 0;
|
||||
opening_settings.start_time += (time[4] - '0') * 1;
|
||||
opening_settings.start_time += (time[3] - '0') * 10;
|
||||
opening_settings.start_time += (time[1] - '0') * 60;
|
||||
opening_settings.start_time += (time[0] - '0') * 600;
|
||||
save_opening_settings(); // save days
|
||||
puts("start time saved\n");
|
||||
} else {
|
||||
puts("provide time in HH:MM format\n");
|
||||
}
|
||||
}
|
||||
printf("start time: %02u:%02u\n", opening_settings.start_time / 60, opening_settings.start_time % 60);
|
||||
}
|
||||
|
||||
/** show/set on which time the access policy stops applying
|
||||
* @param[in] argument string with time of day, optional
|
||||
*/
|
||||
static void command_stop(void* argument)
|
||||
{
|
||||
const char* time = (char*)argument; // argument is optional time
|
||||
if (NULL != argument) { // days are provided, parse and save them
|
||||
bool valid = (5 == strlen(time)); // verify input string
|
||||
if (!(valid && isdigit((int8_t)time[0]) && isdigit((int8_t)time[1]) && ':' == time[2] && isdigit((int8_t)time[3]) && isdigit((int8_t)time[4]))) {
|
||||
valid = false;
|
||||
}
|
||||
if (valid) { // save provided settings
|
||||
opening_settings.stop_time = 0;
|
||||
opening_settings.stop_time += (time[4] - '0') * 1;
|
||||
opening_settings.stop_time += (time[3] - '0') * 10;
|
||||
opening_settings.stop_time += (time[1] - '0') * 60;
|
||||
opening_settings.stop_time += (time[0] - '0') * 600;
|
||||
save_opening_settings(); // save days
|
||||
puts("stop time saved\n");
|
||||
} else {
|
||||
puts("provide time in HH:MM format\n");
|
||||
}
|
||||
}
|
||||
printf("stop time: %02u:%02u\n", opening_settings.stop_time / 60, opening_settings.stop_time % 60);
|
||||
}
|
||||
|
||||
/** open door by simulating button press
|
||||
* @param[in] argument not used
|
||||
*/
|
||||
static void command_open(void* argument)
|
||||
{
|
||||
(void)argument; // we won't use the argument
|
||||
gpio_set(GPIO_PORT(RELAY_PANEL_PIN), GPIO_PIN(RELAY_PANEL_PIN)); // set high to activate relay and take control over the button
|
||||
gpio_set(GPIO_PORT(RELAY_BUTTON_PIN), GPIO_PIN(RELAY_BUTTON_PIN)); // set high to activate relay an simulate button press
|
||||
sleep_ms(1000); // hold button a bit
|
||||
gpio_clear(GPIO_PORT(RELAY_BUTTON_PIN), GPIO_PIN(RELAY_BUTTON_PIN)); // set low to deactivate relay and release button
|
||||
if (!opening_apply) {
|
||||
gpio_clear(GPIO_PORT(RELAY_PANEL_PIN), GPIO_PIN(RELAY_PANEL_PIN)); // set low to deactivate relay and git control back to button
|
||||
}
|
||||
}
|
||||
|
||||
/** show/set button pattern
|
||||
* @param[in] argument sequence of 1/2
|
||||
*/
|
||||
static void command_pattern(void* argument)
|
||||
{
|
||||
const char* pattern = (char*)argument; // argument is optional pattern
|
||||
if (NULL != argument) { // pattern provided
|
||||
bool valid = (LENGTH(opening_settings.button_pattern) >= strlen(pattern)); // verify input string
|
||||
for (uint8_t i = 0; i < strlen(pattern) && valid; i++) {
|
||||
if ('1' != pattern[i] && '2' != pattern[i]) {
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
if (valid) { // save provided settings
|
||||
// reset pattern
|
||||
for (uint8_t i = 0; i < LENGTH(opening_settings.button_pattern); i++) {
|
||||
opening_settings.button_pattern[i] = 0;
|
||||
}
|
||||
// save new pattern
|
||||
for (uint8_t i = 0; i < strlen(pattern); i++) {
|
||||
opening_settings.button_pattern[i] = pattern[i] - '0';
|
||||
}
|
||||
save_opening_settings(); // save days
|
||||
puts("button sequence saved\n");
|
||||
} else {
|
||||
printf("provide buttons sequence of up to %u 1 or 2\n", LENGTH(opening_settings.button_pattern));
|
||||
}
|
||||
for (pattern_length = 0; pattern_length < LENGTH(opening_settings.button_pattern) && opening_settings.button_pattern[pattern_length]; pattern_length++);
|
||||
}
|
||||
if (0 == opening_settings.button_pattern[0]) {
|
||||
puts("no button sequence set\n");
|
||||
} else {
|
||||
puts("button sequence: ");
|
||||
for (uint8_t i = 0; i < LENGTH(opening_settings.button_pattern) && opening_settings.button_pattern[i]; i++) {
|
||||
putc(opening_settings.button_pattern[i] + '0');
|
||||
}
|
||||
putc('\n');
|
||||
}
|
||||
}
|
||||
|
||||
/** test LEDs
|
||||
* @param[in] argument "on" or "off"
|
||||
*/
|
||||
static void command_led(void* argument)
|
||||
{
|
||||
const char* onoff = (char*)argument; // if it should be switched on or off
|
||||
if (NULL == onoff || 0 == strlen(onoff)) {
|
||||
puts("say if the LEDs should be switched on or off\n");
|
||||
} else if (0 == strcmp(onoff, "on")) {
|
||||
for (uint8_t led = 0; led < LED_WS2812B_LEDS; led++) {
|
||||
led_ws2812b_set_rgb(led, 0x20, 0x20 , 0x20);
|
||||
}
|
||||
} else if (0 == strcmp(onoff, "off")) {
|
||||
for (uint8_t led = 0; led < LED_WS2812B_LEDS; led++) {
|
||||
led_ws2812b_set_rgb(led, 0, 0 , 0);
|
||||
}
|
||||
} else {
|
||||
printf("unknown argument %s\n", onoff);
|
||||
}
|
||||
}
|
||||