diff --git a/.article_template.html b/.article_template.html deleted file mode 100644 index 6bd0e9f..0000000 --- a/.article_template.html +++ /dev/null @@ -1,37 +0,0 @@ - - - - chmod777's tilde - - - - - - - - -
-

~chmod777

- -
- -
-

-

-
- - - - diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 index 7ca3725..4b8f2ae --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,8 @@ drafts/* botany_builder/* + +Cargo.lock + +# Added by cargo + +/target diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..3b33914 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "my_blog" +version = "0.1.0" +edition = "2018" + +[dependencies] +tera = "1.12.1" + +serde = { version = "1.0.130", features = ["derive"] } +serde_json = "1.0.64" + diff --git a/assets/css/styles.css b/assets/css/styles.css deleted file mode 100644 index 0c96247..0000000 --- a/assets/css/styles.css +++ /dev/null @@ -1,80 +0,0 @@ -:root { - --background-color: #111; - --border-color: #222; - --text-color: #fb5; - --link-color: #f70; - --link-highlight-color: #f20; - --header-color: #ccc; -} -body { - background-color: var(--background-color); - color: var(--text-color); - display: block; -} -@media screen and (min-width: 800px) { - body { - width: 800px; - } -} -@media screen and (min-width: 1000px) { - body { - margin: auto; - display: grid; - width: 1000px; - grid-template-columns: 200px auto 200px; - grid-template-areas: - 'head head head' - 'nav main .' - 'foot foot foot'; - } - body > header { - grid-area: head; - } - body > nav { - grid-area: nav; - position: sticky; - top: 0; - height: min-content; - } - body > main { - grid-area: main; - } - body > footer { - grid-area: foot; - } -} -h1, h2, h3, h4, h5, h6 { - color: var(--header-color); -} -body > header { - text-align: center; -} -a { - color: var(--link-color); - font-weight: bold; -} -a:hover { - color: var(--link-highlight-color); -} - -img { - max-width: 100%; -} -.icon { - background-color: #aaa; - padding: 5px; - width: 64px; - height: 64px; -} -pre { - background-color: var(--border-color); - color: var(--header-color); - max-width: min-content; - padding: 20px; - border-radius: 5px; -} -footer { - margin: 20px 0px 200px 0px; - text-align: center; -} - diff --git a/config.json b/config.json new file mode 100644 index 0000000..15750b6 --- /dev/null +++ b/config.json @@ -0,0 +1,52 @@ +{ + "posts": [ + { + "url": "/blog/posts/casting_an_aluminium_settlers_of_catan_board.html", + "title": "Casting an Aluminium Settlers of Catan Board", + "date_published": "1 September 2021", + "date_modified": null, + "summary": "", + "template_name": "blog/posts/casting_an_aluminium_settlers_of_catan_board.html" + }, + { + "url": "/blog/posts/pi_pico_first_impressions.html", + "title": "Pi Pico First Impressions", + "date_published": "1 September 2021", + "date_modified": null, + "summary": "", + "template_name": "blog/posts/pi_pico_first_impressions.html" + }, + { + "url": "/blog/posts/designing_my_first_pcb.html", + "title": "Designing my First PCB", + "date_published": "1 September 2021", + "date_modified": null, + "summary": "", + "template_name": "blog/posts/designing_my_first_pcb.html" + } + ], + "archived": [ + + ], + "blogs_that_i_read": [ + + ], + "projects": [ + { + "url": "/projects/project/creative_commons_icons.html", + "title": "Creative Commons Icons", + "image": "/assets/svg/paper-list-icon.svg", + "summary": "Icon assets licensed under CC BY-NC-SA 4.0", + "template_name": "projects/project/creative_commons_icons.html" + } + ], + "tech_demos": [ + { + "url": "/tech_demos/demos/svg_pong.html", + "title": "SVG Pong", + "image": "/assets/svg/pong.svg", + "summary": "The game of Pong using SVG graphics and animations.", + "template_name": "tech_demos/demos/svg_pong.html" + } + ] +} \ No newline at end of file diff --git a/assets/jpeg/catan-mountain-1.jpeg b/public/assets/jpeg/catan-mountain-1.jpeg old mode 100644 new mode 100755 similarity index 100% rename from assets/jpeg/catan-mountain-1.jpeg rename to public/assets/jpeg/catan-mountain-1.jpeg diff --git a/assets/jpeg/catan-mountain-2.jpeg b/public/assets/jpeg/catan-mountain-2.jpeg old mode 100644 new mode 100755 similarity index 100% rename from assets/jpeg/catan-mountain-2.jpeg rename to public/assets/jpeg/catan-mountain-2.jpeg diff --git a/assets/jpeg/catan-mountain-3.jpeg b/public/assets/jpeg/catan-mountain-3.jpeg old mode 100644 new mode 100755 similarity index 100% rename from assets/jpeg/catan-mountain-3.jpeg rename to public/assets/jpeg/catan-mountain-3.jpeg diff --git a/assets/jpeg/nano-pcb-rev1-back.jpeg b/public/assets/jpeg/nano-pcb-rev1-back.jpeg old mode 100644 new mode 100755 similarity index 100% rename from assets/jpeg/nano-pcb-rev1-back.jpeg rename to public/assets/jpeg/nano-pcb-rev1-back.jpeg diff --git a/assets/jpeg/nano-pcb-rev1.jpeg b/public/assets/jpeg/nano-pcb-rev1.jpeg old mode 100644 new mode 100755 similarity index 100% rename from assets/jpeg/nano-pcb-rev1.jpeg rename to public/assets/jpeg/nano-pcb-rev1.jpeg diff --git a/public/assets/js/main.js b/public/assets/js/main.js new file mode 100644 index 0000000..7d17667 --- /dev/null +++ b/public/assets/js/main.js @@ -0,0 +1,62 @@ +const color_theme_id = "color-theme"; +const font_id = "fonts"; + +function save_accessibility_settings(theme, font) { + document.cookie = "theme=" + theme + "; path=/"; + document.cookie = "font=" + font +"; path=/"; +} +function get_saved_accessibility_settings() { + let cookies = document.cookie.split("; "); + let theme = null; + let font = null; + for (let cookie of cookies) { + let [key, value] = cookie.split("="); + console.log("'" + key + "' '" + value + "'"); + if (key == "theme") { + theme = value; + } + if (key == "font") { + font = value; + } + } + if (theme != null) { + let color_theme = document.getElementById(color_theme_id); + for (option of color_theme.options) { + if (option.value == theme) { + option.selected = true; + break; + } + } + } + if (font != null) { + let fonts = document.getElementById(font_id); + for (option of fonts.options) { + if (option.value == font) { + option.selected = true; + break; + } + } + } + return [theme, font]; +} +function load_accessibility_settings(theme, font) { + const body = document.getElementsByTagName("body")[0]; + if (theme.length != 0) { + body.setAttribute("class", theme); + } + if (font.length != 0) { + let class_attribute = body.getAttribute("class"); + body.setAttribute("class", class_attribute+" "+font); + } +} +function apply_accessibility_options() { + const theme = document.getElementById(color_theme_id).value; + const font = document.getElementById(font_id).value; + save_accessibility_settings(theme, font); + load_accessibility_settings(theme, font); +} + +function first_load() { + let [theme, font] = get_saved_accessibility_settings(); + load_accessibility_settings(theme, font); +} \ No newline at end of file diff --git a/assets/svg/chat-bubbles-icon.svg b/public/assets/svg/chat-bubbles-icon.svg old mode 100644 new mode 100755 similarity index 100% rename from assets/svg/chat-bubbles-icon.svg rename to public/assets/svg/chat-bubbles-icon.svg diff --git a/assets/svg/gears-icon.svg b/public/assets/svg/gears-icon.svg old mode 100644 new mode 100755 similarity index 100% rename from assets/svg/gears-icon.svg rename to public/assets/svg/gears-icon.svg diff --git a/assets/svg/paper-list-icon.svg b/public/assets/svg/paper-list-icon.svg old mode 100644 new mode 100755 similarity index 100% rename from assets/svg/paper-list-icon.svg rename to public/assets/svg/paper-list-icon.svg diff --git a/public/assets/svg/pong.svg b/public/assets/svg/pong.svg new file mode 100644 index 0000000..d0bf635 --- /dev/null +++ b/public/assets/svg/pong.svg @@ -0,0 +1,72 @@ + + Pong + Two paddles, a ball, vertical dashed line in the center, and two numbers for player scores. + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + + + + + + \ No newline at end of file diff --git a/public/css/pong_settings.css b/public/css/pong_settings.css new file mode 100644 index 0000000..532f1f8 --- /dev/null +++ b/public/css/pong_settings.css @@ -0,0 +1,16 @@ +fieldset { + width: min-content; + margin-left: auto; + margin-right: auto; +} +fieldset > summary { + text-align:center; +} +fieldset > div { + display: grid; + grid-gap: 5px; + grid-template-columns: max-content min-content; +} +fieldset > div > label { + justify-self: right; +} \ No newline at end of file diff --git a/public/css/styles.css b/public/css/styles.css new file mode 100755 index 0000000..4fea9cd --- /dev/null +++ b/public/css/styles.css @@ -0,0 +1,214 @@ +.default-font { + font-size: 110%; +} +.monospace-font { + font-family: monospace; + font-size: 125%; +} +.dark-theme { + --background-color: #111; + --border-color: #222; + + --text-color: #fb5; + --header-color: #ccc; + + --link-color: #f70; + --link-hover-color: #f20; + --menu-hover-color: #222; + + --post-card-background: #333; +} +.classic-theme { + --background-color: #fff; + --border-color: #222; + + --text-color: #000; + --header-color: #000; + + --link-color: #00f; + --link-hover-color: #00f; + --menu-hover-color: #bbb; + + --post-card-background: #bbb; +} + +body { + background-color: var(--background-color); + color: var(--text-color); + display: block; +} +@media screen and (min-width: 800px) { + body { + width: 800px; + } +} +@media screen and (min-width: 1000px) { + body { + margin: auto; + display: grid; + width: 1000px; + grid-template-columns: auto 1000px auto; + grid-template-areas: + 'header header header' + '. accessibility .' + '. main .' + 'footer footer footer'; + } + body > form { + grid-area: accessibility; + width: max-content; + min-width: min-content; + justify-self: center; + } + body > header { + grid-area: header; + } + body > footer { + grid-area: footer; + } + main { + grid-area: main; + margin: auto; + display: grid; + width: 100%; + grid-gap: 5px; + grid-template-columns: auto 1fr; + grid-template-areas: 'main-aside main-content'; + } + #main-content { + grid-area: main-content; + min-width: min-content; + width: 100%; + } + main > aside { + grid-area: main-aside; + position: sticky; + top: 0; + height: min-content; + max-width: 380px; + } +} +h1, h2, h3, h4, h5, h6 { + color: var(--header-color); +} +.link-list > li, .link-list > li > a, .link-padding { + padding: 10px 10px 10px 10px; +} +body > header { + text-align: center; +} +body > header > nav > ul > li { + display: inline-block; +} +body > header > nav > ul > li > a { + padding: 20px 20px 20px 20px; +} +body > header > nav > ul > li > a:hover { + background-color: var(--menu-hover-color); + border-radius: 8px; +} +body > footer { + margin: 20px 0px 200px 0px; + text-align: center; +} + + + +body > form { + margin-left: auto; + margin-right: auto; + + max-width: 90%; +} +body > form > fieldset { + display: grid; + + width: max-content; + max-width: 90%; + + grid-template-columns: auto 1fr; + grid-gap: 10px; + grid-template-areas: + 'describe describe' + 'color-label color-select' + 'fonts-label fonts-select' + 'apply apply' +} +body > form > fieldset > p { + grid-area: describe; +} +#color-theme-label { + grid-area: color-label; + text-align: left; +} +#color-theme { + grid-area: color-select; +} +#fonts-label { + grid-area: fonts-label; + text-align: left; +} +#fonts { + grid-area: fonts-select; +} +body > form > fieldset > input { + grid-area: apply; + width: max-content; + min-width: min-content; + justify-self: center; +} + + + +main > aside > nav > ul { + padding-left: 15px; +} + +.post-card { + display: grid; + padding: 5px 5px 5px 5px; + background-color: var(--post-card-background); + border-radius: 5px; + grid-template-columns: auto; + grid-template-areas: + 'post-card-title' + 'post-card-published' + 'post-card-modified' + 'post-card-content'; +} +.post-card-title { + grid-area: post-card-title; +} +.post-card-published { + grid-area: post-card-published; +}.post-card-modified { + grid-area: post-card-modified; +}.post-card-content { + grid-area: post-card-content; +} + +a { + color: var(--link-color); + font-weight: bold; +} +a:hover { + color: var(--link-hover-color); +} + +img { + max-width: 100%; +} +.icon { + background-color: #aaa; + padding: 5px; + width: 64px; + height: 64px; +} +pre { + background-color: var(--border-color); + color: var(--header-color); + max-width: min-content; + padding: 20px; + border-radius: 5px; +} + diff --git a/public/feed.rss b/public/feed.rss new file mode 100644 index 0000000..f135312 --- /dev/null +++ b/public/feed.rss @@ -0,0 +1,30 @@ + + + + chmod777's blog + http://tilde.club/~chmod777/blog/blog.html + chmod777's blog where they post about their tech and non tech projects. + + + Casting an Aluminium Settlers of Catan Board + http://tilde.club/~chmod777/blog/posts/casting_an_aluminium_settlers_of_catan_board.html + + + + + + Pi Pico First Impressions + http://tilde.club/~chmod777/blog/posts/pi_pico_first_impressions.html + + + + + + Designing my First PCB + http://tilde.club/~chmod777/blog/posts/designing_my_first_pcb.html + + + + + + \ No newline at end of file diff --git a/public/tech_demos/demos/svg_pong.html b/public/tech_demos/demos/svg_pong.html new file mode 100644 index 0000000..f161a57 --- /dev/null +++ b/public/tech_demos/demos/svg_pong.html @@ -0,0 +1,313 @@ + + + + + + chmod777's SVG Pong + + + + + + + + + + + + + + + + + + + + + + + + + +
+

chmod777's SVG Pong

+ skip to page content +

There is a version of this site available as a gemini capsule. It may or may not be up to date.

+ +
+ +
+
+ Accessibility Options +

accessibility options requires javascript

+ + + + + + + + + +
+
+ + +
+ +
+

The Game of Pong

+
+ +
+
+ +
+
+
+ +
+
+
+

About This Project

+

About three years ago(2018) I created this little project. I have + recently brushed away most of the bugs so here is my Pong clone which uses + Scalable Vector Graphics(SVG).

+ +

Some resources related to SVGs.

+ +
+ +

Just Why

+

The main reason for this project was to learn about SVG and SVG + animations. Before starting this I had never used an SVG in my own + websites. My only interactions with them is seeing them used on other + websites and the one time I opened up InkScape. Back in the days when I + cared about fancy websites I was envious of the cool animations some + folks had and wanted them for myself.

+

The other thing that motivated me to create a game was seeing the 13k game + jam. A jam that only accepts games whos entire contents fit in 13kb + including all their assets. I had only heard about it after it was already + over that year but that wasn't going to stop me from creating a tiny + game.

+

Why Pong?

+

Pong is second only to Tetris and I had never really played it. Plus I thought I + could actually finnish the project if it was a clone of a simple game.

+

Was it a Success?

+

Partially. I did create a game that used an SVG image instead of an + HTMLCanvas. I learned how to manipulate the elements with the transform + attribute from JavaScript, as well as us an animateMotion tag to move the + ball along a path.

+

As a bonus the game was 9kb when compressed when I had "finnished it". + That included the svg, html, and transpiled typescript.

+

However the game wasn't finnished when I stopped working on it three years + ago but I feel I achieved my goal. Or rather the pong game took me as far + as it could on my SVG adventure. Or at least that's what I thought back + then...

+

The Revitalized SVG Pong

+

What you see below is the new and improved SVG Pong, with far fewer bugs, + a cleaner(TM) codebase, and better preformace.

+

I revisted this project because I thought it would be fun. That really is + the main reason. And guess what. It was fun. I got to track down weird + bugs related to collisions and missing state. The project truely was just + slapped together over a weekend and it showed.

+

Try to have fun against the restless AI.

+ +

If you like SVGs checkout some of my SVG icons I made for a now defunct project.

+ +
+
+
+ +

fps:

+ + + Pong + Two paddles, a ball, vertical dashed line in the center, and two numbers for player scores. + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + + + + + + + +
+
+ Controlls + Each button has a keyboard shortcut. +
+ +
+ + + + + + + + + + + + + + +
+
+
+ +
+
+ Game Settings + + +
+ + + + + + + + +
+ + +
+
+ + + Game Summary + + + + + + + + + + + + + + + + + +
DifficultyWonLostTiedIncompleteLongest Volley
Easy
Normal
Hard
+ + Latest Games + + + + + + + +
DifficultyWon/Lost/Tied/IncompletePlayer ScoreAI ScoreLongest Volley
+
+
+ + + + + diff --git a/public/tech_demos/tech_demos.html b/public/tech_demos/tech_demos.html new file mode 100644 index 0000000..9314914 --- /dev/null +++ b/public/tech_demos/tech_demos.html @@ -0,0 +1,90 @@ + + + + + + chmod777's tech demos + + + + + + + + + + + + + + + + + + + + + + + + + +
+

chmod777's tech demos

+ skip to page content +

There is a version of this site available as a gemini capsule. It may or may not be up to date.

+ +
+ +
+
+ Accessibility Options +

accessibility options requires javascript

+ + + + + + + + + +
+
+ + + + + + + + + diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..8de4a0f --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,4 @@ +hard_tabs = true +condense_wildcard_suffixes = true +imports_layout = "Vertical" +max_width = 80 \ No newline at end of file diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..f10df91 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,191 @@ +use std::fs; +use std::fs::File; +use std::io::prelude::*; +use std::path::Path; + +extern crate tera; +use tera::{ + Context, + Tera, +}; + +use serde::{ + Deserialize, + Serialize, +}; + +const BLOG_BASE_TITLE: &'static str = "chmod777's blog: "; + +#[derive(Clone, Debug, Serialize, Deserialize)] +struct Post { + full_url: Option, + url: String, + title: String, + summary: String, + date_published: String, + date_modified: Option, + template_name: String, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +struct TechDemo { + full_url: Option, + url: String, + title: String, + summary: String, + template_name: String, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +struct Config { + posts: Vec, + tech_demos: Vec, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +struct MyContext { + title: String, + description: String, + canonical_url: String, + favicon_image: String, + page_image: String, + + og_type: String, + og_locale: String, + og_site_name: String, + + twitter_card: String, + + posts: Vec, + + tech_demos: Vec, +} +impl Default for MyContext { + fn default() -> Self { + Self { + title: "chmod777's homepage".to_owned(), + description: "A place where chmod777 puts their blog posts, project updates, and tech demos.".to_owned(), + canonical_url: "http://tilde.club/~chmod777".to_owned(), + favicon_image: "http://tilde.club/~chmod777/favicon.svg".to_owned(), + page_image: "http://tilde.club/~chmod777/favicon.svg".to_owned(), + og_type: "website".to_owned(), + og_locale: "en-US".to_owned(), + og_site_name: "tilde.club".to_owned(), + twitter_card: "summary".to_owned(), + posts: Vec::new(), + tech_demos: Vec::new(), + } + } +} +fn write_to_file(path: &str, content: &str) { + let path = Path::new(path); + let display = path.display(); + let mut file = match File::create(path) { + Ok(file) => file, + Err(error) => panic!("Couldn't create file {}: {}", &display, &error), + }; + file.write_all(content.as_bytes()).unwrap(); +} +fn read_file(path: &str) -> String { + fs::read_to_string(path).expect(&format!("Failed to read file: {}", path)) +} +fn append_str(base: &str, extend: &str) -> String { + let mut s = base.to_owned(); + s.push_str(extend); + s +} +fn main() { + let tera = match Tera::new("templates/**/*") { + Ok(tera) => tera, + Err(error_s) => panic!("Parsing error(s): {}", error_s), + }; + + let config_json_string = read_file("config.json"); + let config: Config = serde_json::from_str(&config_json_string).unwrap(); + + let mut context = MyContext::default(); + let base_url = "http://tilde.club/~chmod777"; + for mut post in config.posts.iter().cloned() { + post.full_url = Some(append_str(base_url, &post.url)); + context.posts.push(post); + } + + for mut demo in config.tech_demos.iter().cloned() { + demo.full_url = Some(append_str(base_url, &demo.url)); + context.tech_demos.push(demo) + } + + { + let index = tera + .render("index.html", &Context::from_serialize(&context).unwrap()) + .unwrap(); + write_to_file("public/index.html", &index); + } + + { + context.title = "chmod777's blog".to_owned(); + let blog_context = Context::from_serialize(&context).unwrap(); + let blog = tera + .render( + "blog/blog.html", + &blog_context, + ) + .unwrap(); + write_to_file("public/blog/blog.html", &blog); + + let rss_feed = tera + .render("feed.rss", &blog_context) + .unwrap(); + write_to_file("public/feed.rss", &rss_feed); + + context.og_type = "article".to_owned(); + for post in context.posts.iter() { + context.title = append_str(BLOG_BASE_TITLE, &post.title); + context.canonical_url = post.full_url.clone().unwrap(); + context.description = post.summary.clone(); + + let tera_context = &Context::from_serialize(&context).unwrap(); + let rendered_post = + tera.render(&post.template_name, &tera_context).unwrap(); + let path = { + let mut s = "public".to_owned(); + s.push_str(&post.url); + s + }; + write_to_file(&path, &rendered_post); + } + } + + { + context.title = "chmod777's tech demos".to_owned(); + context.og_type = "website".to_owned(); + context.canonical_url = "".to_owned(); + context.description = "".to_owned(); + + let tech_demos_context = Context::from_serialize(&context).unwrap(); + let tech_demos = tera + .render( + "tech_demos/tech_demos.html", + &tech_demos_context, + ) + .unwrap(); + write_to_file("public/tech_demos/tech_demos.html", &tech_demos); + + for demo in context.tech_demos.iter() { + context.title = append_str("chmod777's ", &demo.title); + context.canonical_url = demo.full_url.clone().unwrap(); + context.description = demo.summary.clone(); + + let demo_context = &Context::from_serialize(&context).unwrap(); + let rendered_demo = + tera.render(&demo.template_name, &demo_context).unwrap(); + let path = { + let mut s = "public".to_owned(); + s.push_str(&demo.url); + s + }; + write_to_file(&path, &rendered_demo); + } + + } +} diff --git a/svg_config.json b/svg_config.json new file mode 100644 index 0000000..c1718d0 --- /dev/null +++ b/svg_config.json @@ -0,0 +1,25 @@ +{ + "svgs": [ + { + "template": "chat-bubbles-icon.svg", + "title": "Chat Bubbles Icon", + "description": "Two overlapping chat bubbles." + }, + { + "template": "gears-icon.svg", + "title": "Gears Icon", + "description": "Two interlocking gears that spin on mouse hover." + }, + { + "template": "paper-list-icon.svg", + "title": "Stacked Papers with Bulleted List Icon", + "description": "Two stacked sheet of paper. A bullet list is depicted on the top sheet." + }, + + { + "template": "pong.svg", + "title": "Pong (Video Game) Table", + "description": "Two rectangle paddles one on either end; A horizontal dashed line and a circle in the center; Boardered by a gradiant from left to right, from green to red." + } + ] +} \ No newline at end of file diff --git a/templates/accessibility_options.html b/templates/accessibility_options.html new file mode 100644 index 0000000..6844b8b --- /dev/null +++ b/templates/accessibility_options.html @@ -0,0 +1,31 @@ +
+
+ Accessibility Options +

accessibility options requires javascript

+ + + + + + + + + +
+
\ No newline at end of file diff --git a/templates/base.html b/templates/base.html new file mode 100755 index 0000000..46ac314 --- /dev/null +++ b/templates/base.html @@ -0,0 +1,18 @@ + + + + +{% include "base_head_inner.html" %} +{% block page_specific_head %}{% endblock page_specific_head %} + + + +{% include "header.html" %} + +{% include "accessibility_options.html" %} + +{% block page_content %}{% endblock page_content%} + +{% include "footer.html" %} + + diff --git a/templates/base_head_inner.html b/templates/base_head_inner.html new file mode 100644 index 0000000..58d5460 --- /dev/null +++ b/templates/base_head_inner.html @@ -0,0 +1,23 @@ + + {{ title | safe }} + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/templates/blog/blog.html b/templates/blog/blog.html new file mode 100644 index 0000000..97c4433 --- /dev/null +++ b/templates/blog/blog.html @@ -0,0 +1,42 @@ +{% extends "base.html" %} +{% import "macros.html" as macros %} + +{% block page_specific_head %} + +{% endblock page_specific_head %} + +{% block page_content %} +
+ + +
+
+

Posts

+ {% for post in posts %} + {{ macros::post_card(post=post) }} + {% endfor %} +
+ +
+

Posts From Blogs That I Read

+
+
+
+{% endblock page_content %} diff --git a/templates/blog/posts/casting_an_aluminium_settlers_of_catan_board.html b/templates/blog/posts/casting_an_aluminium_settlers_of_catan_board.html new file mode 100644 index 0000000..e69de29 diff --git a/templates/blog/posts/designing_my_first_pcb.html b/templates/blog/posts/designing_my_first_pcb.html new file mode 100644 index 0000000..e69de29 diff --git a/templates/blog/posts/pi_pico_first_impressions.html b/templates/blog/posts/pi_pico_first_impressions.html new file mode 100644 index 0000000..5564d07 --- /dev/null +++ b/templates/blog/posts/pi_pico_first_impressions.html @@ -0,0 +1,212 @@ +{% extends "base.html" %} +{% import "macros.html" as macros %} +{% block page_content %} +
+ + +
+
+

Pi Pico First Impressions

+

Specs? Who need those. I wont go deep into the specs because that would be a waist of our time. You can look them up somewhere else.

+ +

What is this dual core ARM M0+ microprocessor good for?

+

+ +

What shouldn't you use it for?

+

This isn't a linux computer like the other Raspberry Pis so don't bother trying to host a server on it or use it to emulate your favorite retro device. Though you certaintly can if you try.

+

It doesn't have wireless connectivity built into it like the esp devices so you have to bring your own at which point you might consider an integrated solution.

+
+ +
+

Language Support

+

The Raspberry Pi foundation supports two languages on the Pi Pico. C and their port of MicroPython to the board.

+

+ + Table summary + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
LanguageBackerRecommended IDEDocumentation
CRaspberry Pi FoundationVisual Studio Code +
    +
  • PDF
  • +
  • Website
  • +
+
MicroPythonRaspberry Pi FoundationThonny +
    +
  • PDF
  • +
  • MicroPython Website
  • +
+
CircuitPythonAdafruitMuPython +
    +
  • Website
  • +
+
Arduino CArduinoArduino IDE +
    +
  • Website
  • +
+
+
+ +
+

Real Applications

+ +

C++ and MicroPython Support

+

To test the language support I created the same program in both MicroPython and C++ using the C SDK. In each program I wrote a driver for the MPU-6050 a sensor with 6 degrees of freedom, and used it to control 2 servos in order to level them.

+ +
+ A Pi Pico on a bread board connected to an MPU-6050 and two servos via jumper wires. +
The MPU-6050 is connected to one of the Pico's i2c busses while the servos are connected to PWM pins.
+
+ +

Programmable IO (PIO)

+

To test the PIO feature of the board I wrote two programs using the C SDK. Each of them poll a key matrix similar to what you would find on a USB keyboard. The first program does it by setting GPIO pins using one of the main cores of the microcontroller while the other polls the keys using the programmable IO state machines.

+ +
+ A wiring diagram for a 3 by 3 keyboard matrix. +
Each row is connected to each column by a diode and a button in parrallel. The diodes are oriented so that current flows from the rows to the columns.
+
+ +

Lets first take a look at the C++ code.

+ + +
+template
+class Keyboard {
+	public:
+	// ... snip ...
+	void poll_buttons() {
+		uint8_t key_index = 0;
+		for (uint8_t col = 0, col_pin = _first_col_pin; col < col_count; col++, col_pin++) {
+			gpio_put(col_pin, 0);
+
+			for (uint8_t row = 0; row < row_count; row++) {
+				const uint8_t row_pin = _first_row_pin + row;
+
+				const bool previous_state{_keys_pressed[key_index]};
+				const bool is_down{gpio_get(row_pin) == 0};
+				_keys_pressed[key_index] = is_down;
+				
+				if (is_down != previous_state) {
+					const auto event_type = static_cast(KeyEventE::KEY_UP - static_cast(is_down));
+					_key_events[_key_event_count] = KeyEvent {event_type, key_index};
+					_key_event_count++;
+				}
+
+				key_index++;
+			}
+
+			gpio_put(col_pin, 1);
+		}
+	}
+	private:
+		std::array _keys_pressed{};
+		std::array _key_events{};
+
+
+ +

Before we can look at the PIO code we first need to review how exactly the Pico's PIO works.

+ + +
+.program keyboard
+
+.define PUBLIC ROW_COUNT 3
+.define PUBLIC COL_COUNT 3
+
+.define COL_0_LOW 0b11110
+.define COL_1_LOW 0b11101
+.define COL_2_LOW 0b11011
+
+; Assume all switches are open.
+; Initialize scratch register X(previous state) to be filled with ones.
+; Ones indicate open switches.
+start:
+	set X, 0
+	mov X, ~X
+	jmp poll
+
+key_changed:
+	; copy current state(scratch register Y) into previous state(scratch register X)
+	mov X, Y
+	; Write the contents of the input shift register(ISR) to the RX FIFO,
+	; also clears the ISR.
+	push
+
+.wrap_target
+poll:
+	; clear the input shift register.
+	; It will be filled with garbage if we didn't push.
+	mov ISR, NULL
+
+	; set column 0 to low all others to high
+	; what 31 cycles to allow the signal to stabilize
+	set PINS, COL_0_LOW [31]
+	; wait another 31 cycles to continue to allow the signal to stabilize
+	nop [31]
+	; read column 0 to the input shift register(ISR)
+	in PINS, ROW_COUNT
+
+	set PINS, COL_1_LOW [31]
+	nop [31]
+	in PINS, ROW_COUNT
+
+	set PINS, COL_2_LOW [31]
+	nop [31]
+	in PINS, ROW_COUNT
+
+	; copy ISR into scratch register Y
+	; flip the bits while copying because the signal is active low
+	mov Y, ~ISR
+
+	; compare current state Y to previous state X
+	; if state changed goto key_changed
+	jmp X != Y, key_changed
+
+	; keys didn't change so poll again
+	.wrap
+
+
+
+
+
+{% endblock page_content %} diff --git a/templates/feed.rss b/templates/feed.rss new file mode 100644 index 0000000..0e68eb1 --- /dev/null +++ b/templates/feed.rss @@ -0,0 +1,18 @@ + + + + chmod777's blog + http://tilde.club/~chmod777/blog/blog.html + chmod777's blog where they post about their tech and non tech projects. + {% for post in posts %} + + {{ post.title | safe }} + {{ post.full_url | safe }} + {{ post.summary | safe }} + {% if post.image %} + {{ post.image | safe }} + {% endif %} + + {% endfor %} + + \ No newline at end of file diff --git a/templates/footer.html b/templates/footer.html new file mode 100644 index 0000000..1389248 --- /dev/null +++ b/templates/footer.html @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/templates/header.html b/templates/header.html new file mode 100644 index 0000000..fa97d87 --- /dev/null +++ b/templates/header.html @@ -0,0 +1,13 @@ +
+

{{ title | safe }}

+ skip to page content +

There is a version of this site available as a gemini capsule. It may or may not be up to date.

+ +
\ No newline at end of file diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..ffc16c1 --- /dev/null +++ b/templates/index.html @@ -0,0 +1,58 @@ +{% extends "base.html" %} +{% import "macros.html" as macros %} +{% block page_content %} +
+ + +
+
+

Introductions

+

Hello!

+

On the internet I go by chmod777. Feel free to ask me anything.

+

A computer science graduate with only vague ideas of what they are doing in life.

+

Lately my life has been revolving around a blue colar job that just ended. In my limited spare time I work on side projects that interest me. They will be the the focus of this blog.

+

If you follow me on mastodon you may have seen my posts about one of my projects Roll Lang. It is a dice notation interpreter with similar syntax to Roll20's. It's getting close to a 1.0 version.

+

I'm interested in contributing to a decentralized web. Currently I'm working on an application that uses conflict-free replicated data types(CRDT) for syncronous editing of a digital Kanban style board.

+

Gardening is a hobby of mine. I'll give an update on the garden when I prune the cherry and apple trees this fall.

+

I've slowly been building an aluminium Settlers of Cattan board out of casted aluminum tiles. This is my longest running project mostly due to the weather, my casting setup, and motivation to deal with both.

+

It's been years since I've used an arduino but I recently bought a Raspberry Pi Pico and have been tinkering with it, as well as diving into KiCad. I've been designing a wireless controller for future electronics projects which may include a wheeled robot, and/or a drone.

+

I've started many Vulkan, OpenGL, and WebGL projects with grand intentions. One of these days I'll get around to getting one of them into a state that I feel is worth sharing.

+
+ +
+

Contact Info

+
    +
  • fediverse/mastodon: @chmod777@tilde.zone
  • +
  • irc.tilde.chat: chmodrwx or chmod777
  • +
  • gitea: tildegit.org/chmod777
  • +
+
+ +
+

Latest Posts

+ {% for post in posts | slice(end=3) %} + {{ macros::post_card(post=post) }} + {% endfor %} + + +
+ +
+

My Botany Plant

+
+
+
+{% endblock page_content %} diff --git a/templates/macros.html b/templates/macros.html new file mode 100644 index 0000000..81b8d7d --- /dev/null +++ b/templates/macros.html @@ -0,0 +1,11 @@ +{% macro post_card(post) %} +
+

{{ post.title | safe }}

+ date published: + {% if post.date_modified %} + last modified: + {% endif %} +

{{ post.summary }}

+
+
+{% endmacro post_card %} \ No newline at end of file diff --git a/templates/tech_demos/demos/about_pong.html b/templates/tech_demos/demos/about_pong.html new file mode 100644 index 0000000..e2252f9 --- /dev/null +++ b/templates/tech_demos/demos/about_pong.html @@ -0,0 +1,9 @@ +
+

The Game of Pong

+
+ +
+
+ +
+
\ No newline at end of file diff --git a/templates/tech_demos/demos/about_svg_pong.html b/templates/tech_demos/demos/about_svg_pong.html new file mode 100644 index 0000000..43438c9 --- /dev/null +++ b/templates/tech_demos/demos/about_svg_pong.html @@ -0,0 +1,54 @@ +
+
+

About This Project

+

About three years ago(2018) I created this little project. I have + recently brushed away most of the bugs so here is my Pong clone which uses + Scalable Vector Graphics(SVG).

+ +

Some resources related to SVGs.

+ +
+ +

Just Why

+

The main reason for this project was to learn about SVG and SVG + animations. Before starting this I had never used an SVG in my own + websites. My only interactions with them is seeing them used on other + websites and the one time I opened up InkScape. Back in the days when I + cared about fancy websites I was envious of the cool animations some + folks had and wanted them for myself.

+

The other thing that motivated me to create a game was seeing the 13k game + jam. A jam that only accepts games whos entire contents fit in 13kb + including all their assets. I had only heard about it after it was already + over that year but that wasn't going to stop me from creating a tiny + game.

+

Why Pong?

+

Pong is second only to Tetris and I had never really played it. Plus I thought I + could actually finnish the project if it was a clone of a simple game.

+

Was it a Success?

+

Partially. I did create a game that used an SVG image instead of an + HTMLCanvas. I learned how to manipulate the elements with the transform + attribute from JavaScript, as well as us an animateMotion tag to move the + ball along a path.

+

As a bonus the game was 9kb when compressed when I had "finnished it". + That included the svg, html, and transpiled typescript.

+

However the game wasn't finnished when I stopped working on it three years + ago but I feel I achieved my goal. Or rather the pong game took me as far + as it could on my SVG adventure. Or at least that's what I thought back + then...

+

The Revitalized SVG Pong

+

What you see below is the new and improved SVG Pong, with far fewer bugs, + a cleaner(TM) codebase, and better preformace.

+

I revisted this project because I thought it would be fun. That really is + the main reason. And guess what. It was fun. I got to track down weird + bugs related to collisions and missing state. The project truely was just + slapped together over a weekend and it showed.

+

Try to have fun against the restless AI.

+ +

If you like SVGs checkout some of my SVG icons I made for a now defunct project.

+ +
+
+
\ No newline at end of file diff --git a/templates/tech_demos/demos/pong.svg b/templates/tech_demos/demos/pong.svg new file mode 100644 index 0000000..d0bf635 --- /dev/null +++ b/templates/tech_demos/demos/pong.svg @@ -0,0 +1,72 @@ + + Pong + Two paddles, a ball, vertical dashed line in the center, and two numbers for player scores. + + + + + + + + + + + + + + + + + + + + + + 0 + 0 + + + + + + + + + + \ No newline at end of file diff --git a/templates/tech_demos/demos/pong_controlls.html b/templates/tech_demos/demos/pong_controlls.html new file mode 100644 index 0000000..be3fb5e --- /dev/null +++ b/templates/tech_demos/demos/pong_controlls.html @@ -0,0 +1,24 @@ +
+
+ Controlls + Each button has a keyboard shortcut. +
+ +
+ + + + + + + + + + + + + + +
+
+
diff --git a/templates/tech_demos/demos/pong_game_summary_table.html b/templates/tech_demos/demos/pong_game_summary_table.html new file mode 100644 index 0000000..7624162 --- /dev/null +++ b/templates/tech_demos/demos/pong_game_summary_table.html @@ -0,0 +1,20 @@ + + Game Summary + + + + + + + + + + + + + + + + + +
DifficultyWonLostTiedIncompleteLongest Volley
Easy
Normal
Hard
\ No newline at end of file diff --git a/templates/tech_demos/demos/pong_latest_games_table.html b/templates/tech_demos/demos/pong_latest_games_table.html new file mode 100644 index 0000000..338c863 --- /dev/null +++ b/templates/tech_demos/demos/pong_latest_games_table.html @@ -0,0 +1,10 @@ + + Latest Games + + + + + + + +
DifficultyWon/Lost/Tied/IncompletePlayer ScoreAI ScoreLongest Volley
\ No newline at end of file diff --git a/templates/tech_demos/demos/pong_settings.html b/templates/tech_demos/demos/pong_settings.html new file mode 100644 index 0000000..5f9c880 --- /dev/null +++ b/templates/tech_demos/demos/pong_settings.html @@ -0,0 +1,23 @@ +
+
+ Game Settings + + +
+ + + + + + + + +
+ + +
+
\ No newline at end of file diff --git a/templates/tech_demos/demos/svg_pong.html b/templates/tech_demos/demos/svg_pong.html new file mode 100644 index 0000000..3a64481 --- /dev/null +++ b/templates/tech_demos/demos/svg_pong.html @@ -0,0 +1,23 @@ +{% extends "base.html" %} + +{% block page_content %} +
+ + {% include "tech_demos/demos/about_pong.html" %} + + +
+ {% include "tech_demos/demos/about_svg_pong.html" %} + +

fps:

+ + {% include "tech_demos/demos/pong.svg" %} + + {% include "tech_demos/demos/pong_controlls.html" %} + {% include "tech_demos/demos/pong_settings.html" %} + + {% include "tech_demos/demos/pong_game_summary_table.html" %} + {% include "tech_demos/demos/pong_latest_games_table.html" %} +
+
+{% endblock page_content %} diff --git a/templates/tech_demos/tech_demos.html b/templates/tech_demos/tech_demos.html new file mode 100644 index 0000000..1733ce7 --- /dev/null +++ b/templates/tech_demos/tech_demos.html @@ -0,0 +1,6 @@ +{% extends "base.html" %} + +{% %} + +{% block page_content %} +{% endblock page_content %} \ No newline at end of file