Move to zola
This commit is contained in:
parent
2c52502f89
commit
065ca39cd6
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/public
|
16
config.toml
Normal file
16
config.toml
Normal file
@ -0,0 +1,16 @@
|
||||
# The URL the site will be built for
|
||||
base_url = "https://fliegendewurst.eu"
|
||||
|
||||
# Whether to automatically compile all Sass files in the sass directory
|
||||
compile_sass = false
|
||||
|
||||
# Whether to build a search index to be used later on by a JavaScript library
|
||||
build_search_index = false
|
||||
|
||||
[markdown]
|
||||
# Whether to do syntax highlighting
|
||||
# Theme can be customised by setting the `highlight_theme` variable to a theme supported by Zola
|
||||
highlight_code = true
|
||||
|
||||
[extra]
|
||||
# Put all your custom variables here
|
6
content/blog/_index.md
Normal file
6
content/blog/_index.md
Normal file
@ -0,0 +1,6 @@
|
||||
+++
|
||||
title = "List of blog posts"
|
||||
sort_by = "date"
|
||||
template = "blog.html"
|
||||
page_template = "blog-page.html"
|
||||
+++
|
@ -1,31 +1,20 @@
|
||||
<!DOCTYPE html>
|
||||
<head>
|
||||
<title>Raspberry Pi temperature monitoring + calendar</title>
|
||||
<meta charset="utf8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
||||
<link rel="shortcut icon" href="favicon.ico">
|
||||
<link rel="stylesheet" href="main.css">
|
||||
</head>
|
||||
+++
|
||||
title = "Raspberry Pi 0 W + sensors + OLED display = temperature monitoring and calendar"
|
||||
date = 2022-10-05
|
||||
updated = 2023-10-02
|
||||
+++
|
||||
|
||||
<body>
|
||||
<main>
|
||||
<h1 id="header"><img src="favicon.ico" id="favicon"/><a href="/">FliegendeWurst's corner of the WWW</a></h1>
|
||||
<img id="hero-img" alt="OLED display showing temperature and upcoming appointments, connected to a Raspberry Pi" src="/assets/raspberry-pi-temperature-monitoring.jpg">
|
||||
|
||||
<h2>Raspberry Pi 0 W + sensors + OLED display = temperature monitoring and calendar</h2>
|
||||
|
||||
<article>
|
||||
|
||||
<img id="hero-img" alt="OLED display showing temperature and upcoming appointments, connected to a Raspberry Pi" src="./raspberry-pi-temperature-monitoring.jpg"/>
|
||||
|
||||
<p>
|
||||
With this project, I monitor the temperature and humidity in my room.
|
||||
Measurements are taken every 10 minutes and saved in an SQLite database.
|
||||
Additionally, the display shows the upcoming appointments of the next five days: each of the 24 rows represents one hour and each pixel in a row represents 5 minutes.
|
||||
In the middle, the time until the next appointment, the relative humidity and the current temperature are shown.
|
||||
Below that, a graph shows the temperature of the last two days.
|
||||
When activating another display mode (not active in the picture), this area of the display instead shows the next seven appointments.
|
||||
</p>
|
||||
<h3>Hardware</h3>
|
||||
|
||||
### Hardware
|
||||
<ul>
|
||||
<li>computer: <a href="https://www.raspberrypi.com/products/raspberry-pi-zero-w/">Raspberry Pi 0 W</a></li>
|
||||
<li>temperature and humidity sensor: AM2302 (wired DHT22)</li>
|
||||
@ -36,43 +25,43 @@ When activating another display mode (not active in the picture), this area of t
|
||||
The components are connected to the Pi as indicated in the wiring diagram below.
|
||||
|
||||
<embed
|
||||
style="background: white;"
|
||||
type="image/svg+xml"
|
||||
src="circuit.svg"
|
||||
title="Wiring diagram" />
|
||||
src="/assets/circuit.svg"
|
||||
title="Wiring diagram">
|
||||
|
||||
<p>
|
||||
Wiring of the SSD1351 to the Pi (<a href="https://pinout.xyz/pinout/spi">pinout</a>):
|
||||
<ol>
|
||||
<li>VCC ↦ 3.3V</li>
|
||||
<li>GND ↦ GND</li>
|
||||
<li>DIN ↦ SPI0 MOSI (GPIO 10)</li>
|
||||
<li>CLK ↦ SPI0 SCLK (GPIO 11)</li>
|
||||
<li>CS ↦ SPI0 CE0 (GPIO 8)</li>
|
||||
<li>DC ↦ GPIO 25</li>
|
||||
<li>RST ↦ GPIO 27</li>
|
||||
</ol>
|
||||
|
||||
1. VCC ↦ 3.3V
|
||||
1. GND ↦ GND
|
||||
1. DIN ↦ SPI0 MOSI (GPIO 10)
|
||||
1. CLK ↦ SPI0 SCLK (GPIO 11)
|
||||
1. CS ↦ SPI0 CE0 (GPIO 8)
|
||||
1. DC ↦ GPIO 25
|
||||
1. RST ↦ GPIO 27
|
||||
|
||||
Wiring of the AM2302 to the RPi:
|
||||
<ol>
|
||||
<li>VCC ↦ 5V</li>
|
||||
<li>second pin ↦ GPIO 26</li>
|
||||
<li>third pin ↦ (unused)</li>
|
||||
<li>GND ↦ GND</li>
|
||||
</ol>
|
||||
|
||||
1. VCC ↦ 5V
|
||||
1. second pin ↦ GPIO 26
|
||||
1. third pin ↦ (unused)
|
||||
1. GND ↦ GND
|
||||
|
||||
Wiring of the push switch to the Pi: one pin to GPIO 19, the other to 3.3V.
|
||||
</p>
|
||||
|
||||
|
||||
<h3>Software</h3>
|
||||
|
||||
<p>
|
||||
|
||||
The Raspberry Pi 0 W is running <a href="https://www.raspberrypi.com/software/">Raspberry Pi OS</a> 11 (bullseye).
|
||||
It is advisable to enable SSH and configure a WLAN network before booting the RPi for the first time.
|
||||
As explained in many tutorials online, this boils down to modifying the SD card image.
|
||||
Create an empty file <span class="code">/boot/ssh</span> to enable the SSH daemon, and configure the credentials of your WLAN network in <span class="code">/etc/wpa_supplicant/wpa_supplicant.conf</span>.
|
||||
<a href="https://valh.io/p/raspberry-pi-configure-wlan/wifi--ssh-before-first-boot/">This blog post</a> describes how to set up the initial user and password.
|
||||
Make sure to set <span class="code">dtparam=spi=on</span> in your <span class="code">/boot/config.txt</span>, otherwise the SPI connection to the OLED display will not work.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
||||
|
||||
All of the following software is written in <a href="https://www.rust-lang.org/">Rust</a> and available <a href="https://github.com/FliegendeWurst/raspi-oled/">on GitHub</a>.
|
||||
<a class="fancy-name" href="https://github.com/FliegendeWurst/raspi-oled/blob/master/src/bin/take_measurement.rs">take_measurements</a> requests the current temperature and humidity from the sensor.
|
||||
To mitigate transmission errors, the median of five readings is used.
|
||||
@ -81,38 +70,36 @@ The final reading is saved in the specified SQLite database (mine is named senso
|
||||
<a class="fancy-name" href="https://github.com/FliegendeWurst/raspi-oled/blob/master/src/bin/status_check_example.rs">status_check</a> gathers some miscellaneus status information on my other computer systems. The provided example just checks whether GitHub and GitLab are up.
|
||||
<a class="fancy-name" href="https://github.com/FliegendeWurst/raspi-oled/blob/master/src/bin/display_all.rs">display_all</a> processes all of this data and displays it on the OLED.
|
||||
It is run by another Python script (<a href="https://gist.github.com/FliegendeWurst/087916a7635c2690de609366517a4ae7">turn_on.py</a>) whenever the push switch is pressed.
|
||||
</p>
|
||||
|
||||
|
||||
Below is the crontab of the pi user. status_check is run every five minutes. take_measurement is run every ten minutes. Fifteen minutes into the hour, any new calendar entries are synced using refresh_json. To avoid OLED burn-in, the display is turned off every minute (if it was on previously).
|
||||
<pre>
|
||||
|
||||
```cron
|
||||
*/5 * * * * /home/pi/status_check /home/pi/sensors.db 2>/dev/null >/dev/null
|
||||
*/10 * * * * /home/pi/take_measurement /home/pi/sensors.db
|
||||
15 * * * * /home/pi/refresh_json --no-weekly
|
||||
* * * * * /home/pi/display_off off
|
||||
</pre>
|
||||
```
|
||||
|
||||
|
||||
<p>
|
||||
For performance reasons (and because the Pi doesn't have enough RAM), I cross-compile these programs using <a href="https://nixos.org/">Nix</a>.
|
||||
I'm certain there's a better way to do it, but the following method works for my purposes:
|
||||
in my <a href="https://github.com/FliegendeWurst/nur-packages/">NUR packages</a>, run the following command to cross-compile all binaries: <span class="code">NIXPKGS_ALLOW_BROKEN=1 nix-build -A raspi-oled-cross</span>.
|
||||
(Btw, this will first compile LLVM and rustc for some reason. There must be a way to avoid that...)
|
||||
As always, <span class="code">./result</span> is a symlink to the build artifacts.
|
||||
The binaries (and the libraries they depend on) may be copied to the Pi like this:
|
||||
<pre>
|
||||
|
||||
```
|
||||
mkdir /tmp/nixstore
|
||||
nix copy --extra-experimental-features nix-command --extra-experimental-features flakes --no-check-sigs --to /tmp/nixstore $(readlink -f result)
|
||||
rsync -r --links --info=progress /tmp/nixstore/nix pi@himbeere-null:~/
|
||||
</pre>
|
||||
```
|
||||
|
||||
On the RPi itself, the binary needs to be adapted to Raspbian and placed in the proper position:
|
||||
<pre>
|
||||
patchelf --set-interpreter /lib/ld-linux-armhf.so.3 nix/store/*-raspi-oled-*/bin/display_all
|
||||
|
||||
```
|
||||
patchelf --set-interpreter /lib/ld-musl-armhf.so.1 nix/store/*-raspi-oled-*/bin/display_all
|
||||
sudo mv nix /
|
||||
</pre>
|
||||
```
|
||||
|
||||
</p>
|
||||
|
||||
<div class="entry-footer">Posted 5th October 2022</div>
|
||||
|
||||
</article>
|
||||
</main>
|
||||
</body>
|
@ -1,21 +1,7 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Fixing the position of TST's new tab button</title>
|
||||
<meta charset="utf8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
||||
<link rel="shortcut icon" href="favicon.ico">
|
||||
<link rel="stylesheet" href="main.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<main>
|
||||
<h1 id="header"><img src="favicon.ico" id="favicon"/><a href="/">FliegendeWurst's corner of the WWW</a></h1>
|
||||
|
||||
<h2>Fixing the position of TST's new tab button</h2>
|
||||
|
||||
<article>
|
||||
|
||||
+++
|
||||
title = "Fixing the position of TST's new tab button"
|
||||
date = 2023-07-30
|
||||
+++
|
||||
<p>
|
||||
Here you can learn how to change the position of the new tab button for the <a href="https://addons.mozilla.org/firefox/addon/tree-style-tab/">Tree Style Tab</a> browser extension.
|
||||
With this change, the new tab button is always at the bottom of the tab tree.
|
||||
@ -26,7 +12,8 @@ No more aiming at a 20 pixels high region below the last opened tab!
|
||||
<p>
|
||||
Simply add the CSS code below to TST's custom style option ("Advanced" > "Extra style rules for contents provided by Tree Style Tab").
|
||||
</p>
|
||||
<pre>
|
||||
|
||||
```css
|
||||
#tabbar .after-tabs {
|
||||
display: none;
|
||||
}
|
||||
@ -38,9 +25,4 @@ Simply add the CSS code below to TST's custom style option ("Advanced" > "Extra
|
||||
:root {
|
||||
--after-tabs-area-size: 20px !important;
|
||||
}
|
||||
</pre>
|
||||
|
||||
</article>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
```
|
@ -1,23 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<head>
|
||||
<title>Solving a logic puzzle using an SMT solver</title>
|
||||
<meta charset="utf8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1"/>
|
||||
<link rel="shortcut icon" href="favicon.ico">
|
||||
<link rel="stylesheet" href="main.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<main>
|
||||
<h1 id="header"><img src="favicon.ico" id="favicon"/><a href="/">FliegendeWurst's corner of the WWW</a></h1>
|
||||
|
||||
<h2>Solving a logic puzzle using an SMT solver</h2>
|
||||
|
||||
<article>
|
||||
+++
|
||||
title = "Solving the Logelei off-by-one puzzle using Z3"
|
||||
date = 2022-11-30
|
||||
+++
|
||||
|
||||
In this blog post, I'll describe how to solve a logic puzzle using Z3, an automated SMT solver.
|
||||
|
||||
<h3>The puzzle</h3>
|
||||
### The puzzle
|
||||
|
||||
<p>
|
||||
You are given a rectangular grid where each cell may either contain a number (between 1 and 5) or be empty.
|
||||
@ -29,7 +17,7 @@ But there's a twist: due to a transmission error, all of the sums are off by one
|
||||
The (already solved) 4-by-4 grid below illustrates these constraints.
|
||||
<br>
|
||||
|
||||
<img src="./sample-puzzle.png" alt="example puzzle" width="400px">
|
||||
<img src="/assets/sample-puzzle.png" alt="example puzzle" width="400px">
|
||||
|
||||
</p>
|
||||
|
||||
@ -49,33 +37,36 @@ The precise installation steps vary by operating system, check the official docu
|
||||
In the following, I will describe the code required to explain the puzzle to Z3.
|
||||
First, we need to import the z3 Python module. To avoid prefixing every constructor with <span class="monospace">z3.</span>, we import all symbols into the local namespace. We also import the math module (we will need it later).
|
||||
We also create a new Solver object for later use.
|
||||
<pre>
|
||||
|
||||
```python
|
||||
from z3 import *
|
||||
import math
|
||||
|
||||
s = Solver()
|
||||
</pre>
|
||||
```
|
||||
Problems are specified by creating a bunch of variables (booleans, integers, ...) and constraints on these variables.
|
||||
Z3 will then produce a <i>model</i> that assigns a value to each variable if the constraints are satisfiable.
|
||||
Since we are interested in the values of the grid cells, we create an integer variable for each cell.
|
||||
<a href="https://z3prover.github.io/api/html/z3.z3.html#-FreshInt">FreshInt</a> returns a new integer variable that isn't identical to any other previously created variable.
|
||||
<pre>
|
||||
|
||||
```python
|
||||
# n*n grid
|
||||
n = 6
|
||||
|
||||
grid = [[FreshInt() for x in range(n)] for y in range(n)]
|
||||
</pre>
|
||||
```
|
||||
|
||||
<h3>Specifying the constraints</h3>
|
||||
|
||||
Empty grid cells will be indicated by a value of zero. All other cells need to be filled with a number between one and five.
|
||||
To implement this constraint, we iterate over each row and cell of the grid and add the constraint (integer value must be at least 0 and at most 5) to the solver.
|
||||
<pre>
|
||||
|
||||
```python
|
||||
for row in grid:
|
||||
for cell in row:
|
||||
s.add(cell >= 0)
|
||||
s.add(cell <= 5)
|
||||
</pre>
|
||||
s.add(cell >= 0)
|
||||
s.add(cell <= 5)
|
||||
```
|
||||
|
||||
For each row/column, the numbers used must be unique.
|
||||
This is modeled by pairwise inequality of the variables making up that row or column.
|
||||
@ -83,34 +74,38 @@ Of course, a value of zero (= empty grid cell) may appear more than once.
|
||||
|
||||
We define a function <span class="monospace">distinct_if_nonzero</span> that, given a list of variables, adds constraints to ensure that any two variables are not equal if both of them are nonzero.
|
||||
This is done by supplying three parameters to the <span class="monospace">If</span> function: the condition, the constraint if the condition is true, the constraint if the condition is false.
|
||||
<pre>
|
||||
|
||||
```python
|
||||
def distinct_if_nonzero(x):
|
||||
for i in range(len(x)):
|
||||
for j in range(i+1, len(x)):
|
||||
cell_i = x[i]
|
||||
cell_j = x[j]
|
||||
s.add(If(And(cell_i != 0, cell_j != 0), cell_i != cell_j, True))
|
||||
</pre>
|
||||
```
|
||||
Then we simply apply this function to each row and column of the grid.
|
||||
<pre>
|
||||
|
||||
```python
|
||||
for row in grid:
|
||||
distinct_if_nonzero(row)
|
||||
|
||||
for column in [[grid[y][x] for y in range(n)] for x in range(n)]:
|
||||
distinct_if_nonzero(column)
|
||||
</pre>
|
||||
```
|
||||
|
||||
The sums indicated next to the grid are represented by integer variables.
|
||||
It is quite obvious that the maximum number of segments possible in each row is achieved if every second cell is filled with a number.
|
||||
As such, we only need to keep track of that many segment sums.
|
||||
<pre>
|
||||
|
||||
```python
|
||||
num_sums = math.ceil(n / 2)
|
||||
sums_h = [[FreshInt() for i in range(num_sums)] for y in range(n)]
|
||||
sums_v = [[FreshInt() for i in range(num_sums)] for x in range(n)]
|
||||
</pre>
|
||||
```
|
||||
|
||||
Connecting these sums to the cell variables is done by considering each binary pattern <span class="monospace">p</span> of numbered / not numbered cells.
|
||||
<pre>
|
||||
|
||||
```python
|
||||
# iterate over each row of the grid
|
||||
for y in range(n):
|
||||
# iterate over each binary pattern
|
||||
@ -123,7 +118,7 @@ for y in range(n):
|
||||
# iterate over each grid cell
|
||||
for x in range(n):
|
||||
# check that binary digit in the pattern
|
||||
if (p >> x) & 1 == 1:
|
||||
if (p >> x) & 1 == 1:
|
||||
mc = And(mc, grid[y][x] != 0)
|
||||
if start_next_segment:
|
||||
segments.append([grid[y][x]])
|
||||
@ -135,7 +130,7 @@ for y in range(n):
|
||||
start_next_segment = True
|
||||
# specify sums value of each segment
|
||||
for k in range(num_sums):
|
||||
if k < len(segments):
|
||||
if k < len(segments):
|
||||
off_by_one = Or(
|
||||
sums_h[y][k] == sum(segments[k]) + 1,
|
||||
sums_h[y][k] == sum(segments[k]) - 1
|
||||
@ -143,14 +138,15 @@ for y in range(n):
|
||||
s.add(If(mc, off_by_one, True))
|
||||
else:
|
||||
s.add(If(mc, sums_h[y][k] == -1, True))
|
||||
</pre>
|
||||
```
|
||||
The same is done for the columns of the grid. For brevity, that code is omitted from this post. The full source code is linked at the end of the article.
|
||||
|
||||
<h3>Finding and displaying a solution</h3>
|
||||
|
||||
We still need to encode the off-by-one sums given in the puzzle as constraints.
|
||||
This is fairly simple:
|
||||
<pre>
|
||||
|
||||
```python
|
||||
def add_spec(spec_h, spec_v):
|
||||
for a, b in zip(spec_h, sums_h):
|
||||
for x, y in zip(a, b):
|
||||
@ -177,7 +173,7 @@ spec_v = [
|
||||
]
|
||||
s.push()
|
||||
add_spec(spec_h, spec_v)
|
||||
</pre>
|
||||
```
|
||||
Note that we used <span class="monospace">s.push()</span> to create a nested constraint context.
|
||||
Any constraints added after this call may be removed by calling <span class="monospace">s.pop()</span>.
|
||||
We will make use of this later to generate new puzzles (with new off-by-one sums).
|
||||
@ -193,7 +189,8 @@ Z3 only returns <span class="monospace">unknown</span> if it is not able to dedu
|
||||
The loop below uses a simple trick to enumerate all solutions to the puzzle: after one solution is found, add a constraint that at least one grid cell must be different.
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
|
||||
```python
|
||||
res = s.check()
|
||||
print(res)
|
||||
|
||||
@ -209,7 +206,7 @@ while res == sat:
|
||||
res = s.check()
|
||||
print("no other solutions")
|
||||
s.pop()
|
||||
</pre>
|
||||
```
|
||||
|
||||
<h3>Generating puzzles with a unique solution</h3>
|
||||
|
||||
@ -223,7 +220,8 @@ If no other solution is found, we know that the puzzle has a unique solution.
|
||||
Otherwise, we add a constraint that bans this particular off-by-one sums configuration (this ensures we always try new puzzles).
|
||||
</p>
|
||||
|
||||
<pre>
|
||||
|
||||
```python
|
||||
# try to find another puzzle with a unique solution
|
||||
while True:
|
||||
s.push()
|
||||
@ -253,12 +251,6 @@ while True:
|
||||
input("continue? [press enter]")
|
||||
s.pop()
|
||||
s.add(c2)
|
||||
</pre>
|
||||
```
|
||||
|
||||
<a href="./logelei_off_by_one_sums.py">Full source code</a>
|
||||
|
||||
<div class="entry-footer">Posted 30th November 2022</div>
|
||||
|
||||
</article>
|
||||
</main>
|
||||
</body>
|
||||
<a href="/assets/logelei_off_by_one_sums.py">Full source code</a>
|
62
main.css
62
main.css
@ -1,62 +0,0 @@
|
||||
#favicon {
|
||||
width: 2.5em;
|
||||
height: 2.5em;
|
||||
margin-right: 0.25em;
|
||||
}
|
||||
|
||||
#header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
article {
|
||||
max-width: 45em;
|
||||
}
|
||||
|
||||
main {
|
||||
max-width: 60em;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
background-color: #d6cebf;
|
||||
border: 0.3em outset gold;
|
||||
border-radius: 1em;
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
#hero-img {
|
||||
width: 30em;
|
||||
padding: 3em;
|
||||
}
|
||||
|
||||
.fancy-name, .code {
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.entry-footer {
|
||||
margin-top: 1em;
|
||||
border-top: 1px solid #005e75;
|
||||
padding-top: 0.5em;
|
||||
}
|
||||
|
||||
@media print {
|
||||
body {
|
||||
background-color: white;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
html {
|
||||
color: #fff;
|
||||
background-color: #000;
|
||||
}
|
||||
a {
|
||||
color: #aaf;
|
||||
}
|
||||
a:visited {
|
||||
color: #faf;
|
||||
}
|
||||
main {
|
||||
background-color: #222;
|
||||
}
|
||||
}
|
4
static/ApollonianCirclePackings/index.html
Normal file
4
static/ApollonianCirclePackings/index.html
Normal file
@ -0,0 +1,4 @@
|
||||
<!DOCTYPE html>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" id="svg" height="800" width="800" viewBox="-500 -500 1000 1000"></svg>
|
||||
<script src="./svg-pan-zoom.min.js"></script>
|
||||
<script src="./index.js"></script>
|
Before Width: | Height: | Size: 154 KiB After Width: | Height: | Size: 154 KiB |
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 160 KiB After Width: | Height: | Size: 160 KiB |
64
static/kv/index.html
Normal file
64
static/kv/index.html
Normal file
@ -0,0 +1,64 @@
|
||||
<!DOCTYPE html><html lang="en"><head>
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>KV diagram calculator</title>
|
||||
<link rel="shortcut icon" href="favicon.png">
|
||||
<script type="module">import init from '/kv/svg-wasm-9785c6ab23ac38e3.js';init('/kv/svg-wasm-9785c6ab23ac38e3_bg.wasm');</script>
|
||||
|
||||
<style>
|
||||
textarea {
|
||||
resize: none;
|
||||
font-size: 32px;
|
||||
line-height: 64px;
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
padding-left: 24px;
|
||||
box-sizing: border-box;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
#input-container {
|
||||
transform: translate(0px, 0px); /* new absolute context */
|
||||
padding-bottom: 1em;
|
||||
}
|
||||
.solution {
|
||||
display: inline-block;
|
||||
}
|
||||
.solution span {
|
||||
display: block;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
h1 {
|
||||
display: inline-block;
|
||||
padding-right: 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<link rel="preload" href="/kv/svg-wasm-9785c6ab23ac38e3_bg.wasm" as="fetch" type="application/wasm" crossorigin="">
|
||||
<link rel="modulepreload" href="/kv/svg-wasm-9785c6ab23ac38e3.js"></head>
|
||||
<body>
|
||||
<div><h1>KV diagram calculator</h1><a href="https://gitlab.kit.edu/uskyk/kv">(source code)</a></div>
|
||||
<div id="main">
|
||||
<div id="settings">
|
||||
<label>Number of variables: <input type="number" min="1" value="3" id="variables"></label><button id="apply-variables">Apply</button>
|
||||
</div>
|
||||
<p id="hints">
|
||||
Change output specification by scrolling (up => 1, down => 0), typing (=> 1, 0) or deleting (=> -).
|
||||
</p>
|
||||
<p id="examples">
|
||||
Load <a href="#--111100">example 1</a>, <a href="#-1111000-1-01---">example 2</a>, <a href="#11101111111111111111101111111101">example 3</a>.
|
||||
Set all cells to <button id="set-0">0</button>, <button id="set-1">1</button> or <button id="set-any">-</button>.
|
||||
</p>
|
||||
<div id="input-container"></div>
|
||||
<div><label>Generate: <select id="mode-select">
|
||||
<option value="dmf">DMF</option>
|
||||
<option value="kmf">KMF</option>
|
||||
</select></label>
|
||||
<label><input type="checkbox" id="labels" checked="">Print variable labels</label></div>
|
||||
<button id="calculate">Run</button>
|
||||
<div id="output-help"></div>
|
||||
<div id="output-container"></div>
|
||||
</div>
|
||||
|
||||
|
||||
</body></html>
|
31
static/qwörtle/index.html
Normal file
31
static/qwörtle/index.html
Normal file
@ -0,0 +1,31 @@
|
||||
<!DOCTYPE html>
|
||||
<meta charset="utf8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Qwörtle</title>
|
||||
<link rel="shortcut icon" href="./favicon.png">
|
||||
<link rel="stylesheet" href="./index.css">
|
||||
|
||||
<div class="centering">
|
||||
<h1>Qwörtle</h1>
|
||||
<div id="main"></div>
|
||||
<pre id="summary"></pre>
|
||||
<button id="share">Teilen <svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path fill="var(--white)" d="M18 16.08c-.76 0-1.44.3-1.96.77L8.91 12.7c.05-.23.09-.46.09-.7s-.04-.47-.09-.7l7.05-4.11c.54.5 1.25.81 2.04.81 1.66 0 3-1.34 3-3s-1.34-3-3-3-3 1.34-3 3c0 .24.04.47.09.7L8.04 9.81C7.5 9.31 6.79 9 6 9c-1.66 0-3 1.34-3 3s1.34 3 3 3c.79 0 1.5-.31 2.04-.81l7.12 4.16c-.05.21-.08.43-.08.65 0 1.61 1.31 2.92 2.92 2.92s2.92-1.31 2.92-2.92c0-1.61-1.31-2.92-2.92-2.92zM18 4c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zM6 13c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1zm12 7.02c-.55 0-1-.45-1-1s.45-1 1-1 1 .45 1 1-.45 1-1 1z"></path></svg></button>
|
||||
<p id="copied-to-clipboard">In die Zwischenablage kopiert!</p>
|
||||
<div id="keyboard">
|
||||
<div class="keyboard-row"></div>
|
||||
<div class="keyboard-row"></div>
|
||||
<div class="keyboard-row"></div>
|
||||
</div>
|
||||
<div id="options">
|
||||
<label><input type="checkbox" id="wide">Breiter Modus</label>
|
||||
<br>
|
||||
<label><input type="checkbox" id="count">Anzahl bei richtigen Buchstaben</label>
|
||||
<div id="secret-options" style="display: none">
|
||||
<br>
|
||||
<label>Anzahl Wörter: <input type="number" id="puzzles" value="4"></label>
|
||||
</div>
|
||||
</div>
|
||||
<textarea id="copyarea"></textarea>
|
||||
</div>
|
||||
|
||||
<script src="./index.js"></script>
|
40
static/sensor-dashboard/index.html
Normal file
40
static/sensor-dashboard/index.html
Normal file
@ -0,0 +1,40 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Sensor dashboard</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<link rel="stylesheet" href="./uPlot.min.css">
|
||||
<link rel="shortcut icon" href="./favicon.ico">
|
||||
<style>
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
#buttons {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
button {
|
||||
padding: 0.5em;
|
||||
margin: 0.75em;
|
||||
font-size: x-large;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="plot"></div>
|
||||
<div id="buttons" style="display: none">
|
||||
<button id="day">24 hours</button>
|
||||
<button id="week">7 days</button>
|
||||
<button id="all">all data</button>
|
||||
</div>
|
||||
<script src="./uPlot.iife.min.js"></script>
|
||||
<script src="./papaparse.js"></script>
|
||||
<script src="./base.js"></script>
|
||||
</body>
|
||||
</html>
|
28
templates/base.html
Normal file
28
templates/base.html
Normal file
@ -0,0 +1,28 @@
|
||||
<!DOCTYPE html>
|
||||
{% block html_tag %} {% endblock %}
|
||||
<head>
|
||||
<title>FliegendeWurst's corner of the WWW</title>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="shortcut icon" href="/favicon.ico">
|
||||
<link rel="stylesheet" href="/main.css">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<main>
|
||||
<h1 id="header"><img src="/favicon.ico" id="favicon" alt="">FliegendeWurst's corner of the WWW</h1>
|
||||
{% block content %} {% endblock %}
|
||||
<hr>
|
||||
<center>
|
||||
<small>
|
||||
© FliegendeWurst, 2022-2023.
|
||||
Source: <a href="https://github.com/FliegendeWurst/fliegendewurst.eu">github.com/FliegendeWurst/fliegendewurst.eu</a>.
|
||||
<br>
|
||||
This work is licensed under a
|
||||
<a rel="license" href="http://creativecommons.org/licenses/by/4.0/">
|
||||
Creative Commons Attribution 4.0 International License</a>.
|
||||
</small>
|
||||
</center>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
15
templates/blog-page.html
Normal file
15
templates/blog-page.html
Normal file
@ -0,0 +1,15 @@
|
||||
{% extends "base.html" %}
|
||||
{% block html_tag %}
|
||||
<html itemscope itemtype="https://schema.org/ItemPage" lang="en-US">
|
||||
{% endblock html_tag %}
|
||||
{% block content %}
|
||||
<article itemid="https://fliegendewurst.eu/blog/{{page.slug}}.html" itemprop="https://schema.org/mainEntity" itemscope itemtype="https://schema.org/BlogPosting">
|
||||
<h2 class="title">
|
||||
{{ page.title }}
|
||||
</h2>
|
||||
<p class="subtitle"><strong>{{ page.date }}{% if page.updated %}
|
||||
(updated <time class="dt-updated mtime updated" datetime="{{page.updated}}T00:00:00Z" itemprop="https://schema.org/dateModified" title="{{page.updated}}">{{page.updated}}</time>)
|
||||
{% endif %}</strong></p>
|
||||
{{ page.content | safe }}
|
||||
</article>
|
||||
{% endblock content %}
|
17
templates/blog.html
Normal file
17
templates/blog.html
Normal file
@ -0,0 +1,17 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block html_tag %}
|
||||
<html lang="en">
|
||||
{% endblock html_tag %}
|
||||
|
||||
{% block content %}
|
||||
<h1 class="title">
|
||||
{{ section.title }}
|
||||
</h1>
|
||||
<ul>
|
||||
<!-- If you are using pagination, section.pages will be empty. You need to use the paginator object -->
|
||||
{% for page in section.pages %}
|
||||
<li><a href="{{ page.permalink | safe }}">{{ page.title }}</a> ({{ page.date }})</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endblock content %}
|
@ -1,27 +1,24 @@
|
||||
<!DOCTYPE html>
|
||||
<title>FliegendeWurst's corner of the WWW</title>
|
||||
<meta charset="utf8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="shortcut icon" href="favicon.ico">
|
||||
<link rel="stylesheet" href="main.css">
|
||||
{% extends "base.html" %}
|
||||
|
||||
<body>
|
||||
<main>
|
||||
<h1 id="header"><img src="favicon.ico" id="favicon">FliegendeWurst's corner of the WWW</h1>
|
||||
{% block html_tag %}
|
||||
<html lang="en">
|
||||
{% endblock html_tag %}
|
||||
|
||||
<span>Interesting tidbits</span>
|
||||
{% block content %}
|
||||
<span>Posts</span>
|
||||
<ul>
|
||||
<li><a href="./z3-logic-puzzle-solving.html">Solving a logic puzzle using an SMT solver</a></li>
|
||||
<li><a href="./tree-style-tabs-new-tab-button-bottom.html">Fixing the position of TST's new tab button</a></li>
|
||||
<li><a href="{{ get_url(path='@/blog/raspberry-pi-temperature-monitoring.md') }}">Raspberry Pi: temperature monitoring and more</a></li>
|
||||
<li><a href="{{ get_url(path='@/blog/tree-style-tabs-new-tab-button-bottom.md') }}">Fixing the position of TST's new tab button</a></li>
|
||||
<li><a href="{{ get_url(path='@/blog/z3-logic-puzzle-solving.md') }}">Solving a logic puzzle using an SMT solver</a></li>
|
||||
<li><a href="https://gist.github.com/FliegendeWurst/6548c71a0f21a60183dbdd2bb7be16db">Repairing the filesystem on my TV's hard disk</a></li>
|
||||
</ul>
|
||||
|
||||
Check out my projects:
|
||||
Projects
|
||||
<ul>
|
||||
<li> <a href="https://fliegendewurst.eu/qw%C3%B6rtle/">Qwörtle</a>, a German version of Quordle</li>
|
||||
<li> <a href="https://github.com/FliegendeWurst/KIT-ILIAS-downloader">KIT-ILIAS-downloader</a>, a tool to download files from the KIT's e-learning site </li>
|
||||
<li> <a href="https://gitlab.com/arnekeller/yt-addiction-control">YT addiction control</a> (<a href="https://addons.mozilla.org/en-US/firefox/addon/youtube-addiction-control/">download</a>), Firefox extension to reduce Youtube's addiction potential by hiding video suggestions </li>
|
||||
<li> <a href="./raspberry-pi-temperature-monitoring.html">raspi-oled</a>, a clock/calendar/temperature display built using a Raspberry Pi </li>
|
||||
<li> <a href="{{ get_url(path='@/blog/raspberry-pi-temperature-monitoring.md') }}">raspi-oled</a>, a clock/calendar/temperature display built using a Raspberry Pi </li>
|
||||
<li> <a href="https://gitlab.kit.edu/uskyk/kv">kv</a>, a small CLI tool / web app to create <a href="https://en.wikipedia.org/wiki/Karnaugh_map">KV diagrams</a> (<a href="https://fliegendewurst.eu/kv/">try it online!</a>) </li>
|
||||
<li> <a href="https://github.com/FliegendeWurst/telegram_notes_bot">telegram_notes_bot</a>, a Telegram bot to interact with a <a href="https://github.com/zadam/trilium">Trilium Notes</a> database (reminders, todos, etc.) </li>
|
||||
<li> <a href="https://github.com/FliegendeWurst/ripgrep-all/tree/mail-adapter">mail adapter</a> for ripgrep-all, really convenient to search mails <i>and</i> their attachments </li>
|
||||
@ -52,5 +49,4 @@ Links: <a href="https://github.com/FliegendeWurst">GitHub profile</a>, <a href="
|
||||
<!--
|
||||
<img src="https://www.boincstats.com/signature/-5/user/86482458946/project/sig.png" alt="BOINC profile">
|
||||
-->
|
||||
</main>
|
||||
</body>
|
||||
{% endblock content %}
|
Loading…
Reference in New Issue
Block a user