Compare commits
157 commits
Author | SHA1 | Date | |
---|---|---|---|
2387aa92a7 | |||
8017182e83 | |||
988a6a2682 | |||
cd10029d0c | |||
8231a7a768 | |||
7fa922c14b | |||
3c15f93ed2 | |||
9c4f388977 | |||
45130cdc0b | |||
5aaaf3c9de | |||
bf96817f9c | |||
78912b2c36 | |||
d07cfb4f21 | |||
|
4433f52bad | ||
|
4a5f56890b | ||
|
6a779606b0 | ||
|
d552722fb6 | ||
|
12a6f475be | ||
591c63bfd1 | |||
|
b4fe8c050a | ||
4990889060 | |||
d4f49408c5 | |||
19310f2265 | |||
222b4e9e54 | |||
bcb94af6a0 | |||
b408ed94f3 | |||
952a9ffd26 | |||
d59498d945 | |||
454bbe5bae | |||
d1ffd3575a | |||
df14cecbd1 | |||
68fc2b6b32 | |||
8f72610b7f | |||
ed3f58a260 | |||
39894e508a | |||
e44dd61d08 | |||
34d8a8bc7e | |||
de54364a0a | |||
c193e721c4 | |||
d2521252c6 | |||
1ea89b7222 | |||
7917ffa204 | |||
d1a316b7ce | |||
9d9ebcc393 | |||
7530bfd328 | |||
047b6b273e | |||
3b2c6e69b7 | |||
f302914622 | |||
670b5874e1 | |||
dba9fd8d36 | |||
aeedf32da3 | |||
509f4935ef | |||
1100eda1bc | |||
8cf30aae75 | |||
f87373c75f | |||
a085a45e3d | |||
b81763a6fa | |||
49795bfbe2 | |||
c6c0c40940 | |||
dc2e675094 | |||
8e3599db0e | |||
e0a7ea6a23 | |||
3f5103d0fe | |||
9640b53abd | |||
bd22e5ccca | |||
471ef862a5 | |||
4c46c78a31 | |||
5a92418b26 | |||
5e10bd18bb | |||
dbd5783e1e | |||
7add1bd406 | |||
d0867bf0ab | |||
5991960f54 | |||
7ce4493514 | |||
fc3442ac30 | |||
6cd2dafd1f | |||
098942ff52 | |||
7dab740083 | |||
2f218fc0ff | |||
ed0b126d31 | |||
f95e254da8 | |||
b1b1f5f826 | |||
2b26d9b4aa | |||
644091f6aa | |||
43aa4cbe75 | |||
2ecc22a18e | |||
0559059a81 | |||
b212f25def | |||
fa0b3bb809 | |||
9176425db2 | |||
72bd72e1f0 | |||
673eeac508 | |||
bf5d7604dc | |||
7a3f49eb96 | |||
31e8d10d1c | |||
a4c56b3ec5 | |||
a497be7098 | |||
271aba65e1 | |||
8dc7d1e5b7 | |||
a5dfe32c11 | |||
0e521b6c51 | |||
269960be45 | |||
ee90a997f1 | |||
23be61fd3e | |||
5dc948c66d | |||
79aba9050a | |||
c211e6cfe0 | |||
cb8481beee | |||
55bd08c4b8 | |||
4e6d5cebd0 | |||
d193a78234 | |||
d6c872e871 | |||
dbf2d9d7f5 | |||
4da09ecdb5 | |||
9a60f0b531 | |||
071f5bf0e1 | |||
10d13a2138 | |||
87d416dbcc | |||
7ff58358fd | |||
a56bba3eb4 | |||
af869d5122 | |||
86e50e2220 | |||
e45de8e12d | |||
9ca1df4fc3 | |||
5a3142e7d8 | |||
2ff3820cc2 | |||
bcd63a261d | |||
46324e4ba6 | |||
bd73e103ee | |||
2b3e282ed6 | |||
daa4488150 | |||
62d262b090 | |||
88db1d2876 | |||
217ff38090 | |||
0a6e09aa18 | |||
5a1744734d | |||
4fa836fe4a | |||
a79235d8c9 | |||
20b0452195 | |||
4445963c6d | |||
70b7a2ef92 | |||
52ea3ed55e | |||
f1936575f5 | |||
20798d4c39 | |||
6d249ed9fe | |||
678af16bbc | |||
36bbe942ae | |||
c832805ea5 | |||
6db295a022 | |||
da595db43d | |||
790c02fff8 | |||
1a57ed743a | |||
f417fa7064 | |||
aefa276aed | |||
2033a13e11 | |||
8f717ded3f | |||
9cf758df6d |
39
.drone.yml
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
kind: pipeline
|
||||||
|
name: default
|
||||||
|
|
||||||
|
platform:
|
||||||
|
os: linux
|
||||||
|
arch: amd64
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: build
|
||||||
|
image: debian:12-slim
|
||||||
|
commands:
|
||||||
|
- apt -qq update
|
||||||
|
- apt -qq install -y -q rsync pandoc wget git
|
||||||
|
- git submodule update --init --recursive
|
||||||
|
- wget -O /tmp/hugo.deb -nv https://github.com/gohugoio/hugo/releases/download/v0.121.1/hugo_extended_0.121.1_linux-amd64.deb
|
||||||
|
- dpkg --install /tmp/hugo.deb
|
||||||
|
- hugo version
|
||||||
|
- hugo --minify
|
||||||
|
|
||||||
|
- name: deploy
|
||||||
|
image: appleboy/drone-scp
|
||||||
|
settings:
|
||||||
|
host: 192.168.4.1
|
||||||
|
target: /opt/${DRONE_REPO_OWNER}/www
|
||||||
|
strip_components: 1
|
||||||
|
source: public/*
|
||||||
|
username: ${DRONE_REPO_OWNER}
|
||||||
|
key:
|
||||||
|
from_secret: ssh_key
|
||||||
|
passphrase:
|
||||||
|
from_secret: ssh_passphrase
|
||||||
|
environment:
|
||||||
|
PLUGIN_DEBUG: "true"
|
||||||
|
when:
|
||||||
|
branch:
|
||||||
|
- master
|
||||||
|
event:
|
||||||
|
exclude:
|
||||||
|
- pull_request
|
1
.gitignore
vendored
|
@ -1,2 +1,3 @@
|
||||||
public/
|
public/
|
||||||
*~
|
*~
|
||||||
|
resources/_gen
|
||||||
|
|
1
.gitmodules
vendored
|
@ -1,3 +1,4 @@
|
||||||
[submodule "themes/academic"]
|
[submodule "themes/academic"]
|
||||||
path = themes/academic
|
path = themes/academic
|
||||||
url = https://github.com/gcushen/hugo-academic.git
|
url = https://github.com/gcushen/hugo-academic.git
|
||||||
|
branch = v4.8.0
|
||||||
|
|
40
Makefile
|
@ -1,40 +0,0 @@
|
||||||
IPNS_BASE = /ipns/juju.net.nz/michaelh/
|
|
||||||
TMPFILE := $(shell tempfile)
|
|
||||||
DESTDIR ?= dest
|
|
||||||
|
|
||||||
all: public
|
|
||||||
|
|
||||||
public:
|
|
||||||
hugo
|
|
||||||
|
|
||||||
ipfs:
|
|
||||||
hugo -b ${IPNS_BASE} -d $@
|
|
||||||
sed -i s#src=\"${IPNS_BASE}#href=\"./# $@/*.html
|
|
||||||
sed -i s#href=\"${IPNS_BASE}#href=\"./# $@/*.html
|
|
||||||
sed -i s#content=\"${IPNS_BASE}#href=\"./# $@/*.html
|
|
||||||
sed -i "s# url('${IPNS_BASE}# url('./#" $@/*.html
|
|
||||||
sed -i s#src=\"${IPNS_BASE}#href=\"../# $@/*/*.html
|
|
||||||
sed -i s#href=\"${IPNS_BASE}#href=\"../# $@/*/*.html
|
|
||||||
sed -i s#src=\"${IPNS_BASE}#href=\"../../# $@/*/*/*.html
|
|
||||||
sed -i s#href=\"${IPNS_BASE}#href=\"../../# $@/*/*/*.html
|
|
||||||
sed -i s#src=\"${IPNS_BASE}#href=\"../../../# $@/*/*/*/*.html
|
|
||||||
sed -i s#href=\"${IPNS_BASE}#href=\"../../../# $@/*/*/*/*.html
|
|
||||||
sed -i s#src=\"${IPNS_BASE}#href=\"../../../../# $@/*/*/*/*/*.html
|
|
||||||
sed -i s#href=\"${IPNS_BASE}#href=\"../../../../# $@/*/*/*/*/*.html
|
|
||||||
|
|
||||||
push: public ipfs
|
|
||||||
chmod -R go+r $^
|
|
||||||
find $^ -type d -exec chmod go+x {} \;
|
|
||||||
rsync -rlt public/ juju@www.juju.nz:michaelh/
|
|
||||||
rm -f $(TMPFILE)
|
|
||||||
|
|
||||||
install: all
|
|
||||||
mkdir -p $(DESTDIR)
|
|
||||||
tar czf $(DESTDIR)/michaelh-wiki+git$(shell git describe --always --tags).tar.gz public
|
|
||||||
|
|
||||||
deps:
|
|
||||||
sudo apt install -y -q hugo rsync
|
|
||||||
|
|
||||||
.PHONY: ipfs public
|
|
||||||
|
|
||||||
# Bump
|
|
197
config.toml
|
@ -1,197 +0,0 @@
|
||||||
baseurl = "https://juju.nz/michaelh/"
|
|
||||||
title = "Blue Duck Valley Rd"
|
|
||||||
copyright = "© 2017 Michael Hope"
|
|
||||||
theme = "academic"
|
|
||||||
enableEmoji = true
|
|
||||||
footnotereturnlinkcontents = "<sup>^</sup>"
|
|
||||||
|
|
||||||
# Enable comments by entering your Disqus shortname
|
|
||||||
disqusShortname = ""
|
|
||||||
|
|
||||||
# Enable analytics by entering your Google Analytics tracking ID
|
|
||||||
googleAnalytics = ""
|
|
||||||
|
|
||||||
# Default language to use (if you setup multilingual support)
|
|
||||||
defaultContentLanguage = "en"
|
|
||||||
defaultContentLanguageInSubdir = false
|
|
||||||
|
|
||||||
[blackfriday]
|
|
||||||
hrefTargetBlank = true
|
|
||||||
|
|
||||||
[params]
|
|
||||||
name = "Michael Hope"
|
|
||||||
role = "Software Engineer, SRE"
|
|
||||||
organization = "Google"
|
|
||||||
organization_url = "https://landing.google.com/sre/"
|
|
||||||
avatar = "mugshot.png"
|
|
||||||
email = "michaelh@juju.nz"
|
|
||||||
address = "Switzerland"
|
|
||||||
# phone = "888 888 88 88"
|
|
||||||
# skype = "echo123"
|
|
||||||
# telegram = ""
|
|
||||||
|
|
||||||
# Enable Keybase in Contact section by entering your keybase.io username.
|
|
||||||
# keybase = "nzmichaelh"
|
|
||||||
|
|
||||||
# Date format (refer to Go's date format: http://flippinggodateformat.com )
|
|
||||||
# Examples: "Mon, Jan 2, 2006" or "2006-01-02"
|
|
||||||
date_format = "Mon, Jan 2, 2006"
|
|
||||||
|
|
||||||
# Enable global LaTeX math rendering?
|
|
||||||
# If false, you can enable it locally on a per page basis.
|
|
||||||
math = false
|
|
||||||
|
|
||||||
# Highlight.js options
|
|
||||||
# highlight
|
|
||||||
# Enable global source code highlighting? If false, you can
|
|
||||||
# override it for a particular page in that page's preamble.
|
|
||||||
#
|
|
||||||
# Example: highlight = true
|
|
||||||
#
|
|
||||||
# highlight_languages
|
|
||||||
# Add support for highlighting additional languages. Support for
|
|
||||||
# languages mentioned here will be included in all pages. You
|
|
||||||
# can also set this variable for a particular page in that
|
|
||||||
# page's preamble.
|
|
||||||
#
|
|
||||||
# Example: highlight_languages = ["go", "lisp", "ocaml"]
|
|
||||||
#
|
|
||||||
# highlight_style
|
|
||||||
# Choose a different CSS style for highlighting source
|
|
||||||
# code. Setting this option in a page's preamble has no
|
|
||||||
# effect.
|
|
||||||
#
|
|
||||||
# Example: highlight_style = "github-gist"
|
|
||||||
#
|
|
||||||
# highlight_version
|
|
||||||
# Choose the version of highlight.js you want. Setting this
|
|
||||||
# option in a page's preamble has no effect.
|
|
||||||
#
|
|
||||||
# Example: highlight_version = "9.9.0"
|
|
||||||
#
|
|
||||||
# For the list of supported languages, styles, and versions, see:
|
|
||||||
# https://cdnjs.com/libraries/highlight.js/
|
|
||||||
#
|
|
||||||
# For more info on the highlighting options, see:
|
|
||||||
# https://gcushen.github.io/hugo-academic-demo/post/writing-markdown-latex/#highlighting-options
|
|
||||||
highlight = true
|
|
||||||
highlight_languages = []
|
|
||||||
# highlight_style = "github"
|
|
||||||
# highlight_version = "9.9.0"
|
|
||||||
|
|
||||||
# Enable native social sharing buttons?
|
|
||||||
sharing = true
|
|
||||||
|
|
||||||
# Link custom CSS and JS assets
|
|
||||||
# (relative to /static/css and /static/js respectively)
|
|
||||||
custom_css = ["michaelh.css"]
|
|
||||||
custom_js = []
|
|
||||||
|
|
||||||
# Publication types.
|
|
||||||
# Used to categorize publications.
|
|
||||||
# The index of the publication type in the list is used as its unique numerical identifier.
|
|
||||||
# The numeric ID is used in a publication's frontmatter to categorize it.
|
|
||||||
# The language can be edited below.
|
|
||||||
# For multi-lingual sites, copy this block to each language section at the end of this file.
|
|
||||||
publication_types = [
|
|
||||||
'Uncategorized', # 0
|
|
||||||
'Conference proceedings', # 1
|
|
||||||
'Journal', # 2
|
|
||||||
'Work in progress', # 3
|
|
||||||
'Technical report', # 4
|
|
||||||
'Book', # 5
|
|
||||||
'Book chapter' # 6
|
|
||||||
]
|
|
||||||
|
|
||||||
# Social/Academic Networking
|
|
||||||
#
|
|
||||||
# Icon pack "fa" includes the following social network icons:
|
|
||||||
#
|
|
||||||
# twitter, weibo, linkedin, github, facebook, pinterest, google-plus,
|
|
||||||
# youtube, instagram, soundcloud
|
|
||||||
#
|
|
||||||
# For email icon, use "fa" icon pack, "envelope" icon, and
|
|
||||||
# "mailto:your@email.com" as the link.
|
|
||||||
#
|
|
||||||
# Full list: https://fortawesome.github.io/Font-Awesome/icons/
|
|
||||||
#
|
|
||||||
# Icon pack "ai" includes the following academic network icons:
|
|
||||||
#
|
|
||||||
# google-scholar, arxiv, orcid, researchgate, mendeley
|
|
||||||
#
|
|
||||||
# Full list: https://jpswalsh.github.io/academicons/
|
|
||||||
|
|
||||||
[[params.social]]
|
|
||||||
icon = "envelope"
|
|
||||||
icon_pack = "fa"
|
|
||||||
link = "mailto:michaelh@juju.nz"
|
|
||||||
|
|
||||||
[[params.social]]
|
|
||||||
icon = "code-fork"
|
|
||||||
icon_pack = "fa"
|
|
||||||
link = "//juju.nz/src/"
|
|
||||||
|
|
||||||
[[params.social]]
|
|
||||||
icon = "github"
|
|
||||||
icon_pack = "fa"
|
|
||||||
link = "//github.com/nzmichaelh"
|
|
||||||
|
|
||||||
[[params.social]]
|
|
||||||
icon = "google-plus"
|
|
||||||
icon_pack = "fa"
|
|
||||||
link = "//plus.google.com/+MichaelHopeX"
|
|
||||||
|
|
||||||
|
|
||||||
# Navigation Links
|
|
||||||
# To link a homepage widget, specify the URL as a hash `#` followed by the filename of the
|
|
||||||
# desired widget in your `content/home/` folder.
|
|
||||||
# The weight parameter defines the order that the links will appear in.
|
|
||||||
|
|
||||||
[[menu.main]]
|
|
||||||
name = "Home"
|
|
||||||
url = "#about"
|
|
||||||
weight = 1
|
|
||||||
|
|
||||||
[[menu.main]]
|
|
||||||
name = "Posts"
|
|
||||||
url = "#posts"
|
|
||||||
weight = 3
|
|
||||||
|
|
||||||
[[menu.main]]
|
|
||||||
name = "Projects"
|
|
||||||
url = "#projects"
|
|
||||||
weight = 4
|
|
||||||
|
|
||||||
[[menu.main]]
|
|
||||||
name = "Notes"
|
|
||||||
url = "#notes"
|
|
||||||
weight = 5
|
|
||||||
|
|
||||||
[[menu.main]]
|
|
||||||
name = "Contact"
|
|
||||||
url = "#contact"
|
|
||||||
weight = 6
|
|
||||||
|
|
||||||
# [[menu.main]]
|
|
||||||
# name = "Publications"
|
|
||||||
# url = "#publications_selected"
|
|
||||||
# weight = 2
|
|
||||||
|
|
||||||
# [[menu.main]]
|
|
||||||
# name = "Teaching"
|
|
||||||
# url = "#teaching"
|
|
||||||
# weight = 5
|
|
||||||
|
|
||||||
|
|
||||||
# Taxonomies.
|
|
||||||
[taxonomies]
|
|
||||||
tag = "tags"
|
|
||||||
category = "categories"
|
|
||||||
publication_type = "publication_types"
|
|
||||||
|
|
||||||
# Languages
|
|
||||||
# Create a [languages.X] block for each language you want, where X is the language ID.
|
|
||||||
|
|
||||||
# Configure the English version of the website.
|
|
||||||
[languages.en]
|
|
||||||
languageCode = "en-us"
|
|
63
config/_default/config.toml
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
# Configuration of Academic
|
||||||
|
# Documentation: https://sourcethemes.com/academic/
|
||||||
|
#
|
||||||
|
# This file is formatted using TOML syntax - learn more at https://learnxinyminutes.com/docs/toml/
|
||||||
|
# Each configuration section is defined by a name in square brackets (e.g. `[outputs]`).
|
||||||
|
|
||||||
|
title = "Blue Duck Valley Rd"
|
||||||
|
baseurl = "https://juju.nz/michaelh/"
|
||||||
|
|
||||||
|
copyright = "© 2017 Michael Hope"
|
||||||
|
|
||||||
|
theme = "academic"
|
||||||
|
enableGitInfo = true
|
||||||
|
defaultContentLanguage = "en"
|
||||||
|
hasCJKLanguage = false # Set `true` for Chinese/Japanese/Korean languages.
|
||||||
|
defaultContentLanguageInSubdir = false
|
||||||
|
removePathAccents = true # Workaround for https://github.com/gohugoio/hugo/issues/5687
|
||||||
|
|
||||||
|
summaryLength = 30 # Listing summary length in words. Also, see `abstract_length` in `params.toml`.
|
||||||
|
paginate = 10 # Number of items per page in paginated lists.
|
||||||
|
enableEmoji = true
|
||||||
|
footnotereturnlinkcontents = "<sup>^</sup>"
|
||||||
|
ignoreFiles = ["\\.ipynb$", ".ipynb_checkpoints$", "\\.Rmd$", "\\.Rmarkdown$", "_files$", "_cache$"]
|
||||||
|
|
||||||
|
[outputs]
|
||||||
|
home = [ "HTML", "RSS", "JSON", "WebAppManifest" ]
|
||||||
|
section = [ "HTML", "RSS" ]
|
||||||
|
|
||||||
|
[mediaTypes."application/manifest+json"]
|
||||||
|
suffixes = ["webmanifest"]
|
||||||
|
|
||||||
|
[outputFormats.WebAppManifest]
|
||||||
|
mediaType = "application/manifest+json"
|
||||||
|
rel = "manifest"
|
||||||
|
|
||||||
|
[markup]
|
||||||
|
defaultMarkdownHandler = "goldmark"
|
||||||
|
[markup.goldmark]
|
||||||
|
[markup.goldmark.renderer]
|
||||||
|
unsafe = true # Enable user to embed HTML snippets in Markdown content.
|
||||||
|
[markup.highlight]
|
||||||
|
codeFences = false # Disable Hugo's code highlighter as it conflicts with Academic's highligher.
|
||||||
|
[markup.tableOfContents]
|
||||||
|
startLevel = 2
|
||||||
|
endLevel = 3
|
||||||
|
|
||||||
|
[imaging]
|
||||||
|
resampleFilter = "lanczos"
|
||||||
|
quality = 90
|
||||||
|
anchor = "smart" # Anchor for cropping. Options include Smart and Center.
|
||||||
|
|
||||||
|
# Taxonomies.
|
||||||
|
[taxonomies]
|
||||||
|
tag = "tags"
|
||||||
|
category = "categories"
|
||||||
|
publication_type = "publication_types"
|
||||||
|
author = "authors"
|
||||||
|
|
||||||
|
[security]
|
||||||
|
enableInlineShortcodes = false
|
||||||
|
[security.exec]
|
||||||
|
allow = ['^(dart-)?sass(-embedded)?$', '^go$', '^npx$', '^postcss$', '^pandoc$']
|
||||||
|
osEnv = ['(?i)^((HTTPS?|NO)_PROXY|PATH(EXT)?|APPDATA|TE?MP|TERM|GO\w+)$']
|
2
config/_default/languages.toml
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
[en]
|
||||||
|
languageCode = "en-us"
|
19
config/_default/menus.toml
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
# Navigation Links
|
||||||
|
# To link a homepage widget, specify the URL as a hash `#` followed by the filename of the
|
||||||
|
# desired widget in your `content/home/` folder.
|
||||||
|
# The weight parameter defines the order that the links will appear in.
|
||||||
|
|
||||||
|
[[main]]
|
||||||
|
name = "Posts"
|
||||||
|
url = "#posts"
|
||||||
|
weight = 20
|
||||||
|
|
||||||
|
[[main]]
|
||||||
|
name = "Projects"
|
||||||
|
url = "#projects"
|
||||||
|
weight = 30
|
||||||
|
|
||||||
|
[[main]]
|
||||||
|
name = "Notes"
|
||||||
|
url = "#notes"
|
||||||
|
weight = 40
|
110
config/_default/params.toml
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
# SITE SETUP
|
||||||
|
# Documentation: https://sourcethemes.com/academic/
|
||||||
|
|
||||||
|
############################
|
||||||
|
## Theme
|
||||||
|
############################
|
||||||
|
|
||||||
|
# Choose a theme.
|
||||||
|
# Latest themes (may require updating): https://sourcethemes.com/academic/themes/
|
||||||
|
# Browse built-in themes in `themes/academic/data/themes/`
|
||||||
|
# Browse user installed themes in `data/themes/`
|
||||||
|
theme = "minimal"
|
||||||
|
|
||||||
|
# Enable users to switch between day and night mode?
|
||||||
|
day_night = false
|
||||||
|
|
||||||
|
# Override the theme's font set (optional).
|
||||||
|
# Latest font sets (may require updating): https://sourcethemes.com/academic/themes/
|
||||||
|
# Browse built-in font sets in `themes/academic/data/fonts/`
|
||||||
|
# Browse user installed font sets in `data/fonts/`
|
||||||
|
font = "minimal"
|
||||||
|
|
||||||
|
# Choose a font size.
|
||||||
|
# Sizes: XS (extra small), S (small), M (medium), L (large - DEFAULT), XL (extra large)
|
||||||
|
font_size = "M"
|
||||||
|
|
||||||
|
############################
|
||||||
|
## Basic Info
|
||||||
|
############################
|
||||||
|
|
||||||
|
# Website type
|
||||||
|
# Improve how search engines understand your site.
|
||||||
|
# For personal sites, choose "Person".
|
||||||
|
# For organizations and projects, choose from https://schema.org/Organization#subtypes
|
||||||
|
# E.g. Person, Organization, LocalBusiness, Project, EducationalOrganization
|
||||||
|
site_type = "Person"
|
||||||
|
|
||||||
|
# Local business type (optional)
|
||||||
|
# If you entered "LocalBusiness" above, choose the type of business from https://schema.org/LocalBusiness#subtypes
|
||||||
|
local_business_type = ""
|
||||||
|
|
||||||
|
# Organization name (optional)
|
||||||
|
# Enter an organization or project name. Defaults to the site title from `config.toml`.
|
||||||
|
org_name = ""
|
||||||
|
|
||||||
|
# Description for social sharing and search engines. If undefined, superuser role is used in place.
|
||||||
|
description = ""
|
||||||
|
|
||||||
|
############################
|
||||||
|
## Site Features
|
||||||
|
############################
|
||||||
|
|
||||||
|
# Enable source code highlighting? true/false
|
||||||
|
# Documentation: https://sourcethemes.com/academic/docs/writing-markdown-latex/#highlighting-options
|
||||||
|
highlight = true
|
||||||
|
|
||||||
|
# Enable LaTeX math rendering? true/false
|
||||||
|
# If false, you can enable math on a per page basis as needed.
|
||||||
|
math = false
|
||||||
|
|
||||||
|
# Enable diagram rendering? true/false
|
||||||
|
# If false, you can enable diagrams on a per page basis as needed.
|
||||||
|
diagram = false
|
||||||
|
|
||||||
|
# Privacy pack
|
||||||
|
# Show a cookie consent message to visitors
|
||||||
|
# Anonymize IP in Google Analytics (if enabled)
|
||||||
|
privacy_pack = false
|
||||||
|
|
||||||
|
email = "michaelh@juju.nz"
|
||||||
|
|
||||||
|
# Address
|
||||||
|
# For country_code, use the 2-letter ISO code (see https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2 )
|
||||||
|
address = {city = "Zürich", country = "Switzerland", country_code = "CH"}
|
||||||
|
|
||||||
|
date_format = "Jan 2, 2006"
|
||||||
|
time_format = "15:04"
|
||||||
|
address_format = "en-us"
|
||||||
|
|
||||||
|
# Main menu alignment (l = left, c = center, r = right) and logo options.
|
||||||
|
main_menu = {align = "l", show_logo = true}
|
||||||
|
reading_time = false
|
||||||
|
sharing = false
|
||||||
|
link_authors = true
|
||||||
|
abstract_length = 135
|
||||||
|
netlify_cms = false
|
||||||
|
|
||||||
|
[avatar]
|
||||||
|
gravatar = false
|
||||||
|
shape = "circle"
|
||||||
|
|
||||||
|
[projects]
|
||||||
|
# Views for associated content.
|
||||||
|
# 1: List
|
||||||
|
# 2: Compact
|
||||||
|
# 3: Card
|
||||||
|
# 4: Citation (publications only)
|
||||||
|
post_view = 2
|
||||||
|
publication_view = 2
|
||||||
|
talk_view = 2
|
||||||
|
|
||||||
|
[comments]
|
||||||
|
engine = 0
|
||||||
|
commentable = {page = true, post = true, docs = true, project = true, publication = true, talk = true}
|
||||||
|
|
||||||
|
[search]
|
||||||
|
engine = 1
|
||||||
|
|
||||||
|
[address_formats]
|
||||||
|
en-us = {order = ['street', 'city', 'region', 'postcode'], delimiters = [', ', ', ', ' ', '']}
|
44
content/authors/michaelh/_index.md
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
---
|
||||||
|
name: Michael Hope
|
||||||
|
|
||||||
|
authors:
|
||||||
|
- michaelh
|
||||||
|
|
||||||
|
superuser: true
|
||||||
|
|
||||||
|
role: Software Engineer
|
||||||
|
|
||||||
|
# Organizations/Affiliations
|
||||||
|
organizations:
|
||||||
|
- name: Google Zürich
|
||||||
|
url: "https://careers.google.com/locations/zurich/"
|
||||||
|
|
||||||
|
interests:
|
||||||
|
- Autonomus machines, especially drones
|
||||||
|
- Developer tools
|
||||||
|
- Languages, and their use in embedded
|
||||||
|
- Embedded software and electronics
|
||||||
|
|
||||||
|
education:
|
||||||
|
courses:
|
||||||
|
- course: Electrical and Electronic Enginnering
|
||||||
|
institution: University of Canterbury
|
||||||
|
- course: Mathematics
|
||||||
|
institution: University of Canterbury
|
||||||
|
|
||||||
|
social:
|
||||||
|
- icon: envelope
|
||||||
|
icon_pack: fas
|
||||||
|
link: 'mailto:michaelh@juju.nz'
|
||||||
|
- icon: code-branch
|
||||||
|
icon_pack: fas
|
||||||
|
link: https://juju.nz/src/
|
||||||
|
- icon: github
|
||||||
|
icon_pack: fab
|
||||||
|
link: https://github.com/nzmichaelh
|
||||||
|
|
||||||
|
# Enter email to display Gravatar (if Gravatar enabled in Config)
|
||||||
|
email: "michaelh@juju.nz"
|
||||||
|
---
|
||||||
|
|
||||||
|
I'm a software engineer from Christchurch, New Zealand now in Zürich, Switzerland.
|
BIN
content/authors/michaelh/avatar.png
Normal file
After Width: | Height: | Size: 17 KiB |
29
content/content/2020/vedirect/index.md
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
---
|
||||||
|
# Documentation: https://sourcethemes.com/academic/docs/managing-content/
|
||||||
|
|
||||||
|
title: "Vedirect"
|
||||||
|
subtitle: ""
|
||||||
|
summary: ""
|
||||||
|
authors: []
|
||||||
|
tags: []
|
||||||
|
categories: []
|
||||||
|
date: 2020-12-01T19:44:14+01:00
|
||||||
|
lastmod: 2020-12-01T19:44:14+01:00
|
||||||
|
featured: false
|
||||||
|
draft: false
|
||||||
|
|
||||||
|
# Featured image
|
||||||
|
# To use, add an image named `featured.jpg/png` to your page's folder.
|
||||||
|
# Focal points: Smart, Center, TopLeft, Top, TopRight, Left, Right, BottomLeft, Bottom, BottomRight.
|
||||||
|
image:
|
||||||
|
caption: ""
|
||||||
|
focal_point: ""
|
||||||
|
preview_only: false
|
||||||
|
|
||||||
|
# Projects (optional).
|
||||||
|
# Associate this post with one or more of your projects.
|
||||||
|
# Simply enter your project's folder or file name without extension.
|
||||||
|
# E.g. `projects = ["internal-project"]` references `content/project/deep-learning/index.md`.
|
||||||
|
# Otherwise, set `projects = []`.
|
||||||
|
projects: []
|
||||||
|
---
|
24
content/content/2021/ausdauer_assembled/index.md
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
---
|
||||||
|
# Documentation: https://sourcethemes.com/academic/docs/managing-content/
|
||||||
|
|
||||||
|
title: "Ausdauer: assembled"
|
||||||
|
subtitle: ""
|
||||||
|
summary: "I've almost completed the right hand side, and the left hand side is far enough along that I can assemble the whole rover!"
|
||||||
|
authors: ['michaelh']
|
||||||
|
tags: []
|
||||||
|
categories: []
|
||||||
|
date: 2021-01-02T17:17:27+01:00
|
||||||
|
lastmod: 2021-01-02T17:17:27+01:00
|
||||||
|
featured: false
|
||||||
|
draft: false
|
||||||
|
|
||||||
|
projects: ['ausdauer']
|
||||||
|
---
|
||||||
|
|
||||||
|
I've almost completed the right hand side, and the left hand side is far enough along that I can assemble the whole rover!
|
||||||
|
|
||||||
|
The remaining steps are:
|
||||||
|
|
||||||
|
1. Epoxy the steering wheel and differential inserts in. I didn't modify the print, and the 3D Jake insets are ~1 mm smaller in diameter.
|
||||||
|
2. Cut the grooves on the left hand side shafts.
|
||||||
|
3. Print the differential.
|
29
content/content/2021/ausdauer_assembled2/index.md
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
---
|
||||||
|
# Documentation: https://sourcethemes.com/academic/docs/managing-content/
|
||||||
|
|
||||||
|
title: "Ausdauer_assembled2"
|
||||||
|
subtitle: ""
|
||||||
|
summary: ""
|
||||||
|
authors: []
|
||||||
|
tags: []
|
||||||
|
categories: []
|
||||||
|
date: 2021-01-05T20:50:00+01:00
|
||||||
|
lastmod: 2021-01-05T20:50:00+01:00
|
||||||
|
featured: false
|
||||||
|
draft: false
|
||||||
|
|
||||||
|
# Featured image
|
||||||
|
# To use, add an image named `featured.jpg/png` to your page's folder.
|
||||||
|
# Focal points: Smart, Center, TopLeft, Top, TopRight, Left, Right, BottomLeft, Bottom, BottomRight.
|
||||||
|
image:
|
||||||
|
caption: ""
|
||||||
|
focal_point: ""
|
||||||
|
preview_only: false
|
||||||
|
|
||||||
|
# Projects (optional).
|
||||||
|
# Associate this post with one or more of your projects.
|
||||||
|
# Simply enter your project's folder or file name without extension.
|
||||||
|
# E.g. `projects = ["internal-project"]` references `content/project/deep-learning/index.md`.
|
||||||
|
# Otherwise, set `projects = []`.
|
||||||
|
projects: []
|
||||||
|
---
|
|
@ -1,53 +1,10 @@
|
||||||
+++
|
+++
|
||||||
# About/Biography widget.
|
# About widget.
|
||||||
|
widget = "about" # See https://sourcethemes.com/academic/docs/page-builder/
|
||||||
|
headless = true # This file represents a page section.
|
||||||
|
active = true # Activate this widget? true/false
|
||||||
|
weight = 20 # Order that this section will appear in.
|
||||||
|
|
||||||
date = "2016-04-20T00:00:00"
|
title = "Biography"
|
||||||
draft = false
|
author = "michaelh"
|
||||||
|
|
||||||
widget = "about"
|
|
||||||
|
|
||||||
# Order that this section will appear in.
|
|
||||||
weight = 1
|
|
||||||
|
|
||||||
# List your academic interests.
|
|
||||||
[interests]
|
|
||||||
interests = [
|
|
||||||
"Autonomus machines, especially drones",
|
|
||||||
"Developer tools",
|
|
||||||
"Languages, and their use in embedded",
|
|
||||||
"Embedded software and electronics",
|
|
||||||
"People management",
|
|
||||||
]
|
|
||||||
|
|
||||||
# List your qualifications (such as academic degrees).
|
|
||||||
[[education.courses]]
|
|
||||||
course = "BE(Hons) Electrical and Electronic Enginnering"
|
|
||||||
institution = "University of Canterbury"
|
|
||||||
year = 1999
|
|
||||||
|
|
||||||
[[education.courses]]
|
|
||||||
course = "BSc Mathematics"
|
|
||||||
institution = "University of Canterbury"
|
|
||||||
year = 1999
|
|
||||||
+++
|
+++
|
||||||
|
|
||||||
# Biography
|
|
||||||
|
|
||||||
I'm a software engineer from Christchurch, New Zealand now in Zürich, Switzerland.
|
|
||||||
|
|
||||||
I'm currently a manager and ex-TPM in
|
|
||||||
[SRE](http://www.reddit.com/r/IAmA/comments/1w1y5m/) at
|
|
||||||
[Google in Zürich](//careers.google.com/locations/zurich/)
|
|
||||||
and before that led the
|
|
||||||
[Linaro](//www.linaro.org/)
|
|
||||||
[Toolchain Working Group](//wiki.linaro.org/WorkingGroups/ToolChain)
|
|
||||||
on [GCC](//gcc.gnu.org/), [GDB](//www.gnu.org/software/gdb/),
|
|
||||||
[QEMU](http://wiki.qemu.org), and everything else developer
|
|
||||||
related.
|
|
||||||
|
|
||||||
My company was [Seabright Technology](http://seabright.co.nz/)
|
|
||||||
and I'm up on [Linkedin](//nz.linkedin.com/pub/michael-hope/10/42/16b).
|
|
||||||
|
|
||||||
Some professional history is in my [[cv]]. There's even a
|
|
||||||
[blog](/michaelh/blog/) which is mirrored up on
|
|
||||||
[G+](//plus.google.com/+MichaelHopeX).
|
|
||||||
|
|
|
@ -1,18 +0,0 @@
|
||||||
+++
|
|
||||||
# Contact widget.
|
|
||||||
|
|
||||||
date = "2016-04-20T00:00:00"
|
|
||||||
draft = false
|
|
||||||
|
|
||||||
title = "Contact"
|
|
||||||
subtitle = ""
|
|
||||||
widget = "contact"
|
|
||||||
|
|
||||||
# Order that this section will appear in.
|
|
||||||
weight = 70
|
|
||||||
|
|
||||||
# Automatically link email and phone?
|
|
||||||
autolink = true
|
|
||||||
|
|
||||||
+++
|
|
||||||
|
|
5
content/home/index.md
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
+++
|
||||||
|
# Homepage
|
||||||
|
type = "widget_page"
|
||||||
|
headless = true # Homepage is headless, other widget pages are not.
|
||||||
|
+++
|
|
@ -1,45 +1,54 @@
|
||||||
+++
|
+++
|
||||||
# Notes widget.
|
widget = "portfolio" # See https://sourcethemes.com/academic/docs/page-builder/
|
||||||
# Note: this widget will only display if `content/note/` contains notes.
|
headless = true # This file represents a page section.
|
||||||
|
active = true # Activate this widget? true/false
|
||||||
date = "2016-04-20T00:00:00"
|
weight = 75 # Order that this section will appear.
|
||||||
draft = false
|
|
||||||
|
|
||||||
title = "Notes"
|
title = "Notes"
|
||||||
subtitle = ""
|
subtitle = ""
|
||||||
widget = "notes"
|
|
||||||
|
|
||||||
# Order that this section will appear in.
|
[content]
|
||||||
weight = 55
|
# Page type to display. E.g. project.
|
||||||
|
page_type = "note"
|
||||||
|
|
||||||
# View.
|
[design]
|
||||||
# Customize how notes are displayed.
|
# Choose how many columns the section has. Valid values: 1 or 2.
|
||||||
# Legend: 0 = list, 1 = cards.
|
columns = "2"
|
||||||
view = 1
|
|
||||||
|
|
||||||
# Filter toolbar.
|
# Toggle between the various page layout types.
|
||||||
# Add or remove as many filters (`[[filter]]` instances) as you like.
|
# 1 = List
|
||||||
# Use "*" tag to show all notes or an existing tag prefixed with "." to filter by specific tag.
|
# 2 = Compact
|
||||||
# To remove toolbar, delete/comment all instances of `[[filter]]` below.
|
# 3 = Card
|
||||||
[[filter]]
|
# 5 = Showcase
|
||||||
name = "All"
|
view = 3
|
||||||
tag = "*"
|
|
||||||
|
|
||||||
[[filter]]
|
# For Showcase view, flip alternate rows?
|
||||||
name = "Machines"
|
flip_alt_rows = false
|
||||||
tag = ".machines"
|
|
||||||
|
|
||||||
[[filter]]
|
[design.background]
|
||||||
name = "Embedded"
|
# Apply a background color, gradient, or image.
|
||||||
tag = ".embedded"
|
# Uncomment (by removing `#`) an option to apply it.
|
||||||
|
# Choose a light or dark text color by setting `text_color_light`.
|
||||||
|
# Any HTML color name or Hex value is valid.
|
||||||
|
|
||||||
[[filter]]
|
# Background color.
|
||||||
name = "Dev tools"
|
# color = "navy"
|
||||||
tag = ".devtools"
|
|
||||||
|
|
||||||
[[filter]]
|
# Background gradient.
|
||||||
name = "People"
|
# gradient_start = "DeepSkyBlue"
|
||||||
tag = ".people"
|
# gradient_end = "SkyBlue"
|
||||||
|
|
||||||
|
# Background image.
|
||||||
|
# image = "background.jpg" # Name of image in `static/img/`.
|
||||||
|
# image_darken = 0.6 # Darken the image? Range 0-1 where 0 is transparent and 1 is opaque.
|
||||||
|
|
||||||
|
# Text color (true=light or false=dark).
|
||||||
|
# text_color_light = true
|
||||||
|
|
||||||
|
[advanced]
|
||||||
|
# Custom CSS.
|
||||||
|
css_style = ""
|
||||||
|
|
||||||
|
# CSS class.
|
||||||
|
css_class = ""
|
||||||
+++
|
+++
|
||||||
|
|
||||||
|
|
|
@ -1,22 +1,68 @@
|
||||||
+++
|
+++
|
||||||
# Recent Posts widget.
|
# A Recent Blog Posts section created with the Pages widget.
|
||||||
# Note: this widget will only display if `content/post/` contains posts.
|
# This section displays recent blog posts from `content/post/`.
|
||||||
|
|
||||||
date = "2016-04-20T00:00:00"
|
widget = "pages" # See https://sourcethemes.com/academic/docs/page-builder/
|
||||||
draft = false
|
headless = true # This file represents a page section.
|
||||||
|
active = true # Activate this widget? true/false
|
||||||
|
weight = 60 # Order that this section will appear.
|
||||||
|
|
||||||
title = "Recent Posts"
|
title = "Recent Posts"
|
||||||
subtitle = ""
|
subtitle = ""
|
||||||
widget = "posts"
|
|
||||||
|
|
||||||
# Order that this section will appear in.
|
[content]
|
||||||
weight = 40
|
# Page type to display. E.g. post, talk, or publication.
|
||||||
|
page_type = "post"
|
||||||
|
|
||||||
# Show posts that contain the following tags. Default to any tags.
|
# Choose how much pages you would like to display (0 = all pages)
|
||||||
tags = []
|
count = 5
|
||||||
|
|
||||||
# Number of posts to list.
|
# Choose how many pages you would like to offset by
|
||||||
count = 5
|
offset = 0
|
||||||
|
|
||||||
|
# Page order. Descending (desc) or ascending (asc) date.
|
||||||
|
order = "desc"
|
||||||
|
|
||||||
|
# Filter posts by a taxonomy term.
|
||||||
|
[content.filters]
|
||||||
|
tag = ""
|
||||||
|
category = ""
|
||||||
|
publication_type = ""
|
||||||
|
author = ""
|
||||||
|
exclude_featured = false
|
||||||
|
|
||||||
|
[design]
|
||||||
|
# Toggle between the various page layout types.
|
||||||
|
# 1 = List
|
||||||
|
# 2 = Compact
|
||||||
|
# 3 = Card
|
||||||
|
# 4 = Citation (publication only)
|
||||||
|
view = 2
|
||||||
|
|
||||||
|
[design.background]
|
||||||
|
# Apply a background color, gradient, or image.
|
||||||
|
# Uncomment (by removing `#`) an option to apply it.
|
||||||
|
# Choose a light or dark text color by setting `text_color_light`.
|
||||||
|
# Any HTML color name or Hex value is valid.
|
||||||
|
|
||||||
|
# Background color.
|
||||||
|
# color = "navy"
|
||||||
|
|
||||||
|
# Background gradient.
|
||||||
|
# gradient_start = "DeepSkyBlue"
|
||||||
|
# gradient_end = "SkyBlue"
|
||||||
|
|
||||||
|
# Background image.
|
||||||
|
# image = "background.jpg" # Name of image in `static/img/`.
|
||||||
|
# image_darken = 0.6 # Darken the image? Range 0-1 where 0 is transparent and 1 is opaque.
|
||||||
|
|
||||||
|
# Text color (true=light or false=dark).
|
||||||
|
# text_color_light = true
|
||||||
|
|
||||||
|
[advanced]
|
||||||
|
# Custom CSS.
|
||||||
|
css_style = ""
|
||||||
|
|
||||||
|
# CSS class.
|
||||||
|
css_class = ""
|
||||||
+++
|
+++
|
||||||
|
|
||||||
|
|
|
@ -1,45 +1,55 @@
|
||||||
+++
|
+++
|
||||||
# Projects widget.
|
# A Projects section created with the Portfolio widget.
|
||||||
# Note: this widget will only display if `content/project/` contains projects.
|
widget = "portfolio" # See https://sourcethemes.com/academic/docs/page-builder/
|
||||||
|
headless = true # This file represents a page section.
|
||||||
date = "2016-04-20T00:00:00"
|
active = true # Activate this widget? true/false
|
||||||
draft = false
|
weight = 65 # Order that this section will appear.
|
||||||
|
|
||||||
title = "Projects"
|
title = "Projects"
|
||||||
subtitle = ""
|
subtitle = ""
|
||||||
widget = "projects"
|
|
||||||
|
|
||||||
# Order that this section will appear in.
|
[content]
|
||||||
weight = 50
|
# Page type to display. E.g. project.
|
||||||
|
page_type = "project"
|
||||||
|
|
||||||
# View.
|
[design]
|
||||||
# Customize how projects are displayed.
|
# Choose how many columns the section has. Valid values: 1 or 2.
|
||||||
# Legend: 0 = list, 1 = cards.
|
columns = "2"
|
||||||
view = 1
|
|
||||||
|
|
||||||
# Filter toolbar.
|
# Toggle between the various page layout types.
|
||||||
# Add or remove as many filters (`[[filter]]` instances) as you like.
|
# 1 = List
|
||||||
# Use "*" tag to show all projects or an existing tag prefixed with "." to filter by specific tag.
|
# 2 = Compact
|
||||||
# To remove toolbar, delete/comment all instances of `[[filter]]` below.
|
# 3 = Card
|
||||||
[[filter]]
|
# 5 = Showcase
|
||||||
name = "All"
|
view = 3
|
||||||
tag = "*"
|
|
||||||
|
|
||||||
[[filter]]
|
# For Showcase view, flip alternate rows?
|
||||||
name = "Machines"
|
flip_alt_rows = false
|
||||||
tag = ".machines"
|
|
||||||
|
|
||||||
[[filter]]
|
[design.background]
|
||||||
name = "Embedded"
|
# Apply a background color, gradient, or image.
|
||||||
tag = ".embedded"
|
# Uncomment (by removing `#`) an option to apply it.
|
||||||
|
# Choose a light or dark text color by setting `text_color_light`.
|
||||||
|
# Any HTML color name or Hex value is valid.
|
||||||
|
|
||||||
[[filter]]
|
# Background color.
|
||||||
name = "Dev tools"
|
# color = "navy"
|
||||||
tag = ".devtools"
|
|
||||||
|
|
||||||
[[filter]]
|
# Background gradient.
|
||||||
name = "People"
|
# gradient_start = "DeepSkyBlue"
|
||||||
tag = ".people"
|
# gradient_end = "SkyBlue"
|
||||||
|
|
||||||
|
# Background image.
|
||||||
|
# image = "background.jpg" # Name of image in `static/img/`.
|
||||||
|
# image_darken = 0.6 # Darken the image? Range 0-1 where 0 is transparent and 1 is opaque.
|
||||||
|
|
||||||
|
# Text color (true=light or false=dark).
|
||||||
|
# text_color_light = true
|
||||||
|
|
||||||
|
[advanced]
|
||||||
|
# Custom CSS.
|
||||||
|
css_style = ""
|
||||||
|
|
||||||
|
# CSS class.
|
||||||
|
css_class = ""
|
||||||
+++
|
+++
|
||||||
|
|
||||||
|
|
|
@ -1,22 +0,0 @@
|
||||||
+++
|
|
||||||
# Recent Publications widget.
|
|
||||||
# Note: this widget will only display if `content/publication/` contains publications.
|
|
||||||
|
|
||||||
date = "2016-04-20T00:00:00"
|
|
||||||
draft = true
|
|
||||||
|
|
||||||
title = "Recent Publications"
|
|
||||||
subtitle = ""
|
|
||||||
widget = "publications"
|
|
||||||
|
|
||||||
# Order that this section will appear in.
|
|
||||||
weight = 20
|
|
||||||
|
|
||||||
# Number of publications to list.
|
|
||||||
count = 10
|
|
||||||
|
|
||||||
# Show publication details (such as abstract)? (true/false)
|
|
||||||
detailed_list = false
|
|
||||||
|
|
||||||
+++
|
|
||||||
|
|
|
@ -1,20 +0,0 @@
|
||||||
+++
|
|
||||||
# Selected Publications widget.
|
|
||||||
# Note: this widget will only display if `content/publication/` contains publications
|
|
||||||
# with `selected = true` in their `+++` preamble.
|
|
||||||
|
|
||||||
date = "2016-04-20T00:00:00"
|
|
||||||
draft = true
|
|
||||||
|
|
||||||
title = "Selected Publications"
|
|
||||||
subtitle = ""
|
|
||||||
widget = "publications_selected"
|
|
||||||
|
|
||||||
# Order that this section will appear in.
|
|
||||||
weight = 10
|
|
||||||
|
|
||||||
# Show publication details (such as abstract)? (true/false)
|
|
||||||
detailed_list = true
|
|
||||||
|
|
||||||
+++
|
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
+++
|
|
||||||
# Recent and Upcoming Talks widget.
|
|
||||||
|
|
||||||
date = "2016-04-20T00:00:00"
|
|
||||||
draft = true
|
|
||||||
|
|
||||||
title = "Recent & Upcoming Talks"
|
|
||||||
subtitle = ""
|
|
||||||
widget = "talks"
|
|
||||||
|
|
||||||
# Order that this section will appear in.
|
|
||||||
weight = 30
|
|
||||||
|
|
||||||
# Number of talks to list.
|
|
||||||
count = 10
|
|
||||||
|
|
||||||
# Show talk details (such as abstract)? (true/false)
|
|
||||||
detailed_list = false
|
|
||||||
|
|
||||||
+++
|
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
+++
|
|
||||||
# An example of using the custom widget to create your own homepage section.
|
|
||||||
# To create more sections, duplicate this file and edit the values below as desired.
|
|
||||||
|
|
||||||
date = "2016-04-20T00:00:00"
|
|
||||||
draft = true
|
|
||||||
|
|
||||||
title = "Teaching"
|
|
||||||
subtitle = ""
|
|
||||||
widget = "custom"
|
|
||||||
|
|
||||||
# Order that this section will appear in.
|
|
||||||
weight = 60
|
|
||||||
|
|
||||||
+++
|
|
||||||
|
|
||||||
This is an example of using the *custom* widget to create your own homepage section.
|
|
||||||
|
|
||||||
I am a teaching instructor for the following courses at University X:
|
|
||||||
|
|
||||||
- CS101: An intro to computer science
|
|
||||||
- CS102: An intro to computer science
|
|
||||||
- CS103: An intro to computer science
|
|
||||||
- CS104: An intro to computer science
|
|
||||||
- CS105: An intro to computer science
|
|
||||||
- CS106: An intro to computer science
|
|
||||||
- CS107: An intro to computer science
|
|
|
@ -10,7 +10,7 @@ categories:
|
||||||
---
|
---
|
||||||
A certain website had a few vuneribilities including XSS and leaking passwords. The fixes were:
|
A certain website had a few vuneribilities including XSS and leaking passwords. The fixes were:
|
||||||
|
|
||||||
* The <script> tag was turned on for pending users. Configure off.  All other users get their tags filtered against a safe list
|
* The `<script>` tag was turned on for pending users. Configure off.  All other users get their tags filtered against a safe list
|
||||||
* The superuser always skips the filter and sees all tags. Â I can’t fix this, but I’ve changed the cookie so that its not useful to a cookie catcher
|
* The superuser always skips the filter and sees all tags. Â I can’t fix this, but I’ve changed the cookie so that its not useful to a cookie catcher
|
||||||
* The ‘password’ in the cookie was just a hash of the password. It is now a hash of the password, the IP address of the client, and a secret.  A leaked password should only be usable from the same IP
|
* The ‘password’ in the cookie was just a hash of the password. It is now a hash of the password, the IP address of the client, and a secret.  A leaked password should only be usable from the same IP
|
||||||
* The ‘password’ field has been removed from all forms and replaced with cookie based authentication
|
* The ‘password’ field has been removed from all forms and replaced with cookie based authentication
|
|
@ -3,14 +3,14 @@ title: Z-84 flying wing
|
||||||
author: michaelh
|
author: michaelh
|
||||||
type: post
|
type: post
|
||||||
date: 2015-10-03T15:49:55+00:00
|
date: 2015-10-03T15:49:55+00:00
|
||||||
url: /2015/10/z-84-flying-wing/
|
#url: /2015/10/z-84-flying-wing/
|
||||||
categories:
|
categories:
|
||||||
- Flying
|
- Flying
|
||||||
|
|
||||||
---
|
---
|
||||||
Hobbyking had a free shipping promotion recently, so I picked up a [Wing Wing Z-84 flying wing][1] like [all the cool kids have][2].
|
Hobbyking had a free shipping promotion recently, so I picked up a [Wing Wing Z-84 flying wing][1] like [all the cool kids have][2].
|
||||||
|
|
||||||
[<img src="https://juju.net.nz/michaelh/blog/wp-content/uploads/2015/10/IMG_20151003_171416.jpg" alt="Z-84 flying wing" width="2448" height="2506" class="aligncenter size-full wp-image-284" />][3]
|
![Z-84 flying wing](IMG_20151003_171416.jpg)
|
||||||
|
|
||||||
It’s quite fun if a bit noisy. The 1500 mAh 2S batter lasts 15 minutes, which is crazy long, and it’s quite interesting how a pusher propeller changes climbs and loops – you need momentum as there’s no propeller to caster under.
|
It’s quite fun if a bit noisy. The 1500 mAh 2S batter lasts 15 minutes, which is crazy long, and it’s quite interesting how a pusher propeller changes climbs and loops – you need momentum as there’s no propeller to caster under.
|
||||||
|
|
||||||
|
@ -18,4 +18,3 @@ Next step is to change the motor controller for the one that the PX4 team recomm
|
||||||
|
|
||||||
[1]: http://www.hobbyking.com/hobbyking/store/__25664__Wing_Wing_Z_84_EPO_845mm_PNF_.html
|
[1]: http://www.hobbyking.com/hobbyking/store/__25664__Wing_Wing_Z_84_EPO_845mm_PNF_.html
|
||||||
[2]: https://pixhawk.org/platforms/planes/z-84_wing_wing
|
[2]: https://pixhawk.org/platforms/planes/z-84_wing_wing
|
||||||
[3]: https://juju.net.nz/michaelh/blog/wp-content/uploads/2015/10/IMG_20151003_171416.jpg
|
|
BIN
content/post/2015/10/z84/IMG_20151003_171416.jpg
Normal file
After Width: | Height: | Size: 135 KiB |
BIN
content/post/2020/adapter/adapter.png
Normal file
After Width: | Height: | Size: 45 KiB |
1
content/post/2020/adapter/featured.png
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
adapter.png
|
18
content/post/2020/adapter/index.md
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
+++
|
||||||
|
date = "2020-09-04T00:02:42+02:00"
|
||||||
|
title = "Propeller adapter"
|
||||||
|
highlight = true
|
||||||
|
draft = false
|
||||||
|
+++
|
||||||
|
|
||||||
|
A nice thing about having a 3D printer is quick fixes to small
|
||||||
|
things. The 8x4 propeller that came with my [Bixler 3][bixler3] was
|
||||||
|
loose on the motor shaft, so I 3D printed an adapter:
|
||||||
|
|
||||||
|
![Propeller adapter](./adapter.png)
|
||||||
|
|
||||||
|
It took 5 minutes to print and three tries to get the tolerances
|
||||||
|
right, but the propeller is now a good fit, much quieter, and seems
|
||||||
|
more powerful. Yay!
|
||||||
|
|
||||||
|
[bixler3]: https://hobbyking.com/en_us/h-king-bixler-3-glider-1550-pnf.html
|
BIN
content/post/2020/ausdauer_left/featured.jpg
Normal file
After Width: | Height: | Size: 114 KiB |
15
content/post/2020/ausdauer_left/index.md
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
---
|
||||||
|
title: "Ausdauer: left rocker/bogie assembly"
|
||||||
|
subtitle: ""
|
||||||
|
summary: "Progress update showing the new left hand assembly"
|
||||||
|
authors: ["michaelh"]
|
||||||
|
categories: []
|
||||||
|
date: 2020-12-27T18:35:54+01:00
|
||||||
|
lastmod: 2020-12-27T18:35:54+01:00
|
||||||
|
draft: false
|
||||||
|
projects: ["ausdauer"]
|
||||||
|
---
|
||||||
|
|
||||||
|
I'm working on a build of the [Sawppy rover](https://github.com/Roger-random/Sawppy_Rover). Today was a major milestone, as I finished assembling the left hand rocker/bogie assembly. This covers all of the major parts and techniques needed to build the rover, so the remaining work is to crank the handle and get the right side and box done.
|
||||||
|
|
||||||
|
There's more information at the [build log](https://juju.nz/michaelh/project/ausdauer/).
|
BIN
content/post/2020/autobix/20200824_171641.jpg
Normal file
After Width: | Height: | Size: 431 KiB |
BIN
content/post/2020/autobix/20200824_171653.jpg
Normal file
After Width: | Height: | Size: 371 KiB |
BIN
content/post/2020/autobix/ap.png
Normal file
After Width: | Height: | Size: 74 KiB |
1
content/post/2020/autobix/featured.jpg
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
20200824_171653.jpg
|
45
content/post/2020/autobix/index.md
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
+++
|
||||||
|
date = "2020-08-29T00:02:42+02:00"
|
||||||
|
title = "Bixler 3 Drone"
|
||||||
|
highlight = true
|
||||||
|
draft = false
|
||||||
|
+++
|
||||||
|
|
||||||
|
I recently purchased a HobbyKing [Bixler 3][bixler3] to use as a autopilot
|
||||||
|
platform and am pretty happy with how it turned out. Both the under
|
||||||
|
wing area and nose area have plenty of room and could easily fit a
|
||||||
|
[Pixhawk 4 Mini][p4m] with GPS and telemetry.
|
||||||
|
|
||||||
|
I prefer the Pixhawk 4 Mini over the older [Pixfalcon][pixfalcon] for
|
||||||
|
a few reasons:
|
||||||
|
|
||||||
|
- the servo connectors are built-in to the module
|
||||||
|
- the safety switch is built-in to the GPS
|
||||||
|
- two of the four sides are connector free, making it easier to route
|
||||||
|
|
||||||
|
I ended up putting the power distribution board in the under wing area
|
||||||
|
and 3D printing a hatch to hold everything else:
|
||||||
|
|
||||||
|
![Hatch](./ap.png)
|
||||||
|
|
||||||
|
The hatch is held on by the front tongue and 2x magnets. The
|
||||||
|
autopilot, telemetry, and RX are on the bottom of the hatch and the
|
||||||
|
GPS on top:
|
||||||
|
|
||||||
|
![Cropped to autopliot](./20200824_171653.jpg)
|
||||||
|
|
||||||
|
The plane flew just fine in stabilised and hold mode with the default
|
||||||
|
PX4 aircraft turnings. I was a bit surprised by how PX4 handles a
|
||||||
|
strong headwind - if the plane can't make progress in hold mode at the
|
||||||
|
cruise throttle then it gives up and switches back to stabilised
|
||||||
|
mode.
|
||||||
|
|
||||||
|
![Overall](./20200824_171641.jpg)
|
||||||
|
|
||||||
|
Once the weather clears up I hope to do some waypoint tests. I'd also
|
||||||
|
like to find a quieter propeller. The stock is fine, but it's
|
||||||
|
slightly unbalanced and could be much quieter.
|
||||||
|
|
||||||
|
[bixler3]: https://hobbyking.com/en_us/h-king-bixler-3-glider-1550-pnf.html
|
||||||
|
[p4m]: https://shop.holybro.com/pixhawk4-mini_p1120.html
|
||||||
|
[pixfalcon]: https://docs.px4.io/v1.9.0/en/flight_controller/pixfalcon.html
|
1
content/post/2020/diskio/featured.png
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
iosecs.png
|
68
content/post/2020/diskio/index.md
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
---
|
||||||
|
date: "2020-11-26T00:02:42+02:00"
|
||||||
|
title: "Reducing disk IO"
|
||||||
|
highlight: true
|
||||||
|
draft: false
|
||||||
|
projects: ["solarch"]
|
||||||
|
---
|
||||||
|
|
||||||
|
I'm using a Raspberry Pi 4 with a USB3 based SSD to host this website
|
||||||
|
and other services, and as a side effect the lounge looks like a disco
|
||||||
|
due to all the flashing activity LEDs.
|
||||||
|
|
||||||
|
The machine should be fairly idle so I went looking for where the disk
|
||||||
|
activity was coming from.
|
||||||
|
|
||||||
|
Running `iotop -a` and looking at the writes shows that mysql (aka
|
||||||
|
MariaDB) is by far the heaviest writer.
|
||||||
|
|
||||||
|
Looking at the metrics recorded by the Prometheus
|
||||||
|
[mysqld\_exporter](https://github.com/prometheus/mysqld_exporter):
|
||||||
|
|
||||||
|
```
|
||||||
|
delta(mysql_global_status_commands_total[1h])
|
||||||
|
```
|
||||||
|
|
||||||
|
gives:
|
||||||
|
|
||||||
|
![MariaDB commands per hour](mysql.png)
|
||||||
|
|
||||||
|
which is dominated by inserts.
|
||||||
|
|
||||||
|
Dropping to the mysql command line and running:
|
||||||
|
|
||||||
|
```
|
||||||
|
SELECT table_name, table_rows from
|
||||||
|
INFORMATION_SCHEMA.TABLES ORDER BY table_rows DESC LIMIT 20;
|
||||||
|
```
|
||||||
|
|
||||||
|
gives:
|
||||||
|
|
||||||
|
| table_name | table\_rows |
|
||||||
|
|------------------------------------------------|------------|
|
||||||
|
| events | 1731748 |
|
||||||
|
| states | 1577498 |
|
||||||
|
| oc\_filecache | 26239 |
|
||||||
|
| notice | 23483 |
|
||||||
|
|
||||||
|
The `events` and `states` tables are part of my
|
||||||
|
[HomeAssistant](https://www.home-assistant.io/) installation. Reading
|
||||||
|
the docs shows that the `recorder` commits every second and changing this
|
||||||
|
to 5m using:
|
||||||
|
|
||||||
|
```
|
||||||
|
recorder:
|
||||||
|
db_url: mysql+pymysql://hass:....@db/hass?charset=utf8
|
||||||
|
commit_interval: 300
|
||||||
|
```
|
||||||
|
|
||||||
|
fixes the problem. This reduces the write rate from 680 KiB/s to
|
||||||
|
~95 KiB/s:
|
||||||
|
|
||||||
|
![Bytes written per 10 minutes](writes.png)
|
||||||
|
|
||||||
|
and cuts the I/O seconds per second:
|
||||||
|
|
||||||
|
![I/O seconds per second](iosecs.png)
|
||||||
|
|
||||||
|
and makes the lounge much less blinky, yay!
|
BIN
content/post/2020/diskio/iosecs.png
Normal file
After Width: | Height: | Size: 25 KiB |
BIN
content/post/2020/diskio/mysql.png
Normal file
After Width: | Height: | Size: 26 KiB |
BIN
content/post/2020/diskio/writes.png
Normal file
After Width: | Height: | Size: 25 KiB |
40
content/post/2020/esphome.md
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
+++
|
||||||
|
date = "2020-02-01T00:02:42+02:00"
|
||||||
|
title = "ESPHome as a Ruuvi bridge"
|
||||||
|
highlight = true
|
||||||
|
draft = false
|
||||||
|
+++
|
||||||
|
|
||||||
|
[ESPHome](https://esphome.io/) is a framework for building custom home
|
||||||
|
automation that runs on an ESP8266 or
|
||||||
|
[ESP32][esp32]. It's pretty cool - you
|
||||||
|
select and configure components by writing a [YAML
|
||||||
|
file][cfg], which
|
||||||
|
then drives host side [Python
|
||||||
|
snippets][snip]
|
||||||
|
to configure and bind the device side code, which is then built and
|
||||||
|
pushed using [PlatformIO](https://platformio.org/).
|
||||||
|
|
||||||
|
[esp32]: https://en.wikipedia.org/wiki/ESP32
|
||||||
|
[cfg]: https://esphome.io/cookbook/display_time_temp_oled.html
|
||||||
|
[snip]: https://github.com/esphome/esphome/blob/dev/esphome/components/ruuvitag/sensor.py
|
||||||
|
|
||||||
|
Some nice touches:
|
||||||
|
|
||||||
|
- It integrates with [Home Assistant](https://home-assistant.io/)
|
||||||
|
and has automatic discovery
|
||||||
|
- It can drive displays, including [rendering TTF files][ttf]
|
||||||
|
host side to give nice fonts device side
|
||||||
|
- It looks reasonably composable so, for example, you can have
|
||||||
|
multiple Bluetooth broadcast parsers
|
||||||
|
|
||||||
|
[ttf]: https://esphome.io/cookbook/display_time_temp_oled.html#define-the-fonts
|
||||||
|
I used this to bridge between [Ruuvi tags](https://ruuvi.com/) and my
|
||||||
|
Home Assistant instance. Home Assistant then re-exports everything to
|
||||||
|
[Prometheus](https://prometheus.io/).
|
||||||
|
|
||||||
|
It also gave me a reason to break out the 3D printer and make a case
|
||||||
|
for the ESP-WROOM-32 based
|
||||||
|
[Lolin32](https://wiki.wemos.cc/products:lolin32:lolin32):
|
||||||
|
|
||||||
|
![Case](./case.jpg)
|
BIN
content/post/2020/esphome/case.jpg
Normal file
After Width: | Height: | Size: 20 KiB |
55
content/post/2020/kiefer_pine.md
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
+++
|
||||||
|
date = "2020-04-11T00:00:00+02:00"
|
||||||
|
title = "Self-contained and smoke"
|
||||||
|
highlight = true
|
||||||
|
draft = false
|
||||||
|
tags = ["kieferbot"]
|
||||||
|
+++
|
||||||
|
|
||||||
|
A day of highs and lows. Here's Kieferbot still in [teleop][teleop]
|
||||||
|
mode but now self contained:
|
||||||
|
|
||||||
|
<video controls src="self.m4v"></video>
|
||||||
|
|
||||||
|
The [PinePhone][pinephone] is taking commands from a PS4 DualShock
|
||||||
|
over Bluetooth using `ds4drv` and sending them to the IO board over
|
||||||
|
serial. I experimented with a few transports:
|
||||||
|
|
||||||
|
- UDP over Wifi was fine, but the default ESP32 Wifi power mode added
|
||||||
|
random latency of up to 100 ms. Switching to 'always on' mode by
|
||||||
|
modifying MicroPython fixed this.
|
||||||
|
- Bluetooth Low Energy worked for one-way communication to the IO
|
||||||
|
board, but there was lots of packet loss when using Bluetooth for
|
||||||
|
both the joystick and IO.
|
||||||
|
|
||||||
|
Inspired by [Firmata][firmata], I settled on MIDI over serial. I
|
||||||
|
quite like the MIDI format for short messages. A frame starts with
|
||||||
|
the command followed by a command specific payload. The command has
|
||||||
|
the MSB set so it's simple to frame.
|
||||||
|
|
||||||
|
I also compiled [ROS Kinetic][kinetic] for Debian Buster and created an IO node
|
||||||
|
that takes left and right velocity commands. There seems to be an
|
||||||
|
ordering issue with either the ESP32 PWM chip or the FIT0441
|
||||||
|
controller, where going from forward to reverse and vice-versa is
|
||||||
|
fine but the motor won't start sometimes when going from zero.
|
||||||
|
|
||||||
|
So plenty of highs, but the low was [releasing the smoke][smoke] - I'm
|
||||||
|
using a LiPo battery for the supply and something short-circuited when
|
||||||
|
unplugging the battery. It seems to have taken out the regulator and
|
||||||
|
the ESP32 flash.
|
||||||
|
|
||||||
|
Next step is to re-make the IO board using an [ItsyBitsy M4][m4]. It
|
||||||
|
also runs MicroPython so the IO software was easy to port. I've
|
||||||
|
ordered some Seeed [XIAO][xiao] boards - $5 gives you a MicroPython
|
||||||
|
compatible SAMD21 with 9 pins of IO which is enough for this project.
|
||||||
|
|
||||||
|
This time I'll secure the battery socket better and 3D print a
|
||||||
|
shield.
|
||||||
|
|
||||||
|
[pinephone]: https://wiki.pine64.org/index.php/PinePhone
|
||||||
|
[teleop]: https://en.wikipedia.org/wiki/Teleoperation
|
||||||
|
[firmata]: https://github.com/firmata/protocol
|
||||||
|
[xiao]: https://www.seeedstudio.com/Seeeduino-XIAO-Arduino-Microcontroller-SAMD21-Cortex-M0+-p-4426.html
|
||||||
|
[kinetic]: http://wiki.ros.org/kinetic
|
||||||
|
[smoke]: https://en.wikipedia.org/wiki/Magic_smoke
|
||||||
|
[m4]: https://www.adafruit.com/product/3800
|
BIN
content/post/2020/kiefer_pine/self.m4v
Normal file
26
content/post/2020/kiefer_telop/index.md
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
+++
|
||||||
|
date = "2020-03-22T00:00:00+02:00"
|
||||||
|
title = "Teleop"
|
||||||
|
highlight = true
|
||||||
|
draft = false
|
||||||
|
tags = ["kieferbot"]
|
||||||
|
+++
|
||||||
|
|
||||||
|
As a winter project, I'm turning my
|
||||||
|
[PinePhone](https://wiki.pine64.org/index.php/PinePhone) into an
|
||||||
|
indoor robot. The Pinephone has everything needed for a decent
|
||||||
|
stand-alone robot: plenty of CPU, a camera,
|
||||||
|
gyro/accelerometor/compass, display, and it can run Debian.
|
||||||
|
|
||||||
|
Below is a video of the hardware in [teleop](https://en.wikipedia.org/wiki/Teleoperation) mode. A
|
||||||
|
[Lolin32](https://wiki.wemos.cc/products:lolin32:lolin32)
|
||||||
|
[ESP32](https://www.espressif.com/en/products/hardware/esp32/overview)
|
||||||
|
running [MicroPython](https://micropython) takes commands over
|
||||||
|
Bluetooth LE and sends PWM and direction signals to the
|
||||||
|
[FIT0441](https://wiki.dfrobot.com/FIT0441_Brushless_DC_Motor_with_Encoder_12V_159RPM)
|
||||||
|
motors. The frame is 3D printed.
|
||||||
|
|
||||||
|
<video controls src="teleop.m4v"></video>
|
||||||
|
|
||||||
|
Currently the phone is just a passenger. The next step is to get the
|
||||||
|
phone to communicate with the IO board.
|
BIN
content/post/2020/kiefer_telop/teleop.m4v
Normal file
BIN
content/post/2020/nppilot/above.jpg
Normal file
After Width: | Height: | Size: 220 KiB |
1
content/post/2020/nppilot/featured.jpg
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
above.jpg
|
27
content/post/2020/nppilot/index.md
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
---
|
||||||
|
title: "nppilot"
|
||||||
|
subtitle: "My never complete, often restarted rover"
|
||||||
|
summary: ""
|
||||||
|
date: 2020-11-27T17:23:01+01:00
|
||||||
|
lastmod: 2020-11-27T17:23:01+01:00
|
||||||
|
featured: true
|
||||||
|
draft: false
|
||||||
|
projects: ["nppilot"]
|
||||||
|
---
|
||||||
|
|
||||||
|
I realized I haven't posted about the current generation of `nppilot`,
|
||||||
|
my never complete, often restarted rover project.
|
||||||
|
|
||||||
|
The current version is a modified Traxxas E-Revo 1/16:
|
||||||
|
|
||||||
|
Under the hood this is:
|
||||||
|
|
||||||
|
* A Revo controller running ROSflight for control and sensors.
|
||||||
|
* A Raspberry Pi 3 running ROS for command and telemetry.
|
||||||
|
|
||||||
|
The frame is laser cut MDF with 3D printed mounts and
|
||||||
|
risers.
|
||||||
|
|
||||||
|
It works well, but winter came before I made much progress on the
|
||||||
|
control. The goal is for the Pi to do the planning using the GPS and a
|
||||||
|
map of the local area, and to see how fast it can complete the course.
|
59
content/post/2020/pudel.md
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
+++
|
||||||
|
date = "2020-05-23T00:00:00+02:00"
|
||||||
|
title = "Pan/tilt mount for a cell phone"
|
||||||
|
highlight = true
|
||||||
|
draft = false
|
||||||
|
tags = ["pudel", "ros"]
|
||||||
|
+++
|
||||||
|
|
||||||
|
I think it's important to also post about the projects that didn't
|
||||||
|
turn out. Pudel was a project to automatically track and video a
|
||||||
|
model plane using a cell phone camera.
|
||||||
|
|
||||||
|
![mount](mount.jpg)
|
||||||
|
|
||||||
|
The pan and tilt components were modeled in
|
||||||
|
[OpenSCAD](https://openscad.org/) and 3D printed. The pan unit had a
|
||||||
|
standard [1/4-20 tripod mount][tri] and the tilt unit had a Samsung
|
||||||
|
A40 mount that I've used on a few other projects.
|
||||||
|
|
||||||
|
[tri]: https://www.matrix-vision.com/manuals/mvBlueFOX/mvBF_page_tech.html
|
||||||
|
![close](closeup.jpg)
|
||||||
|
|
||||||
|
Both were driven by standard [HS-422][hs422] servos with a [Trinket M0][m0]
|
||||||
|
for I/O and a 2S LiPo through a [6V BEC][bec] for power.
|
||||||
|
|
||||||
|
The I/O board ran [CircuitPython][cp] with custom firmware that takes
|
||||||
|
MIDI-like messages as servo commands. It implemented an
|
||||||
|
acceleration limiter and velocity limiter to limit the
|
||||||
|
[jerk][jerk], as the servos are capable of 60 deg/0.2 s which is much
|
||||||
|
faster than the plane would move.
|
||||||
|
|
||||||
|
The host side was going to be similar to Kieferbot and use ROS with an
|
||||||
|
IO specific node, OpenCV based detection, and a PID controller to keep
|
||||||
|
the plane centered in the frame.
|
||||||
|
|
||||||
|
So why did it fail?
|
||||||
|
|
||||||
|
- The servos aren't intended for precise, slow movement and hence had
|
||||||
|
mild jerk.
|
||||||
|
- There was too much slack in the mechanics, and the phone was mildly
|
||||||
|
heavy, so mild jerk caused the phone to move about too much.
|
||||||
|
- The plane is too small to resolve on a wide angle lens, so tracking
|
||||||
|
was unlikely to work.
|
||||||
|
|
||||||
|
If I wanted to take it further, I can imagine some improvements:
|
||||||
|
|
||||||
|
- Take the load off the pan servo. The tilt unit sits directly on top
|
||||||
|
of the pan servo shaft and there's not much material to sit on which
|
||||||
|
increased the shake. Perhaps add a turntable style plates that most
|
||||||
|
of the downwards force could go through.
|
||||||
|
- Decrease the arm length out to the phone. It's 30 mm and reducing
|
||||||
|
to 20 mm would reduce the load on the tilt servo.
|
||||||
|
- Try a phone with optical stabilization.
|
||||||
|
|
||||||
|
[bec]: https://hobbyking.com/en_us/turnigy-3a-ubec-w-noise-reduction.html?___store=en_us
|
||||||
|
[cp]: https://circuitpython.org/
|
||||||
|
[hs422]: https://hitecrcd.com/products/servos/sport-servos/analog-sport-servos/hs-422/product
|
||||||
|
[jerk]: https://en.wikipedia.org/wiki/Jerk_(physics)
|
||||||
|
[m0]: https://www.adafruit.com/product/3500
|
BIN
content/post/2020/pudel/closeup.jpg
Normal file
After Width: | Height: | Size: 84 KiB |
BIN
content/post/2020/pudel/mount.jpg
Normal file
After Width: | Height: | Size: 71 KiB |
101
content/post/2020/rawpizero.md
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
+++
|
||||||
|
date = "2020-02-09T00:02:42+02:00"
|
||||||
|
title = "Bare metal Raspberry Pi Zero"
|
||||||
|
highlight = true
|
||||||
|
draft = false
|
||||||
|
+++
|
||||||
|
These are short notes on loading baremetal software on the Raspberry
|
||||||
|
Pi Zero. I started down this path due to wanting to learn Rust, which
|
||||||
|
lead to [Tock](https://www.tockos.org/) which is an embedded operating
|
||||||
|
system in Rust. Rather than buy yet another piece of hardware I
|
||||||
|
though I'd use one of my 5 (!) Raspberry Pi Zeros as a development
|
||||||
|
board instead. It's just another microcontroller, just one that runs
|
||||||
|
at 1 GHz and has a ton of RAM...
|
||||||
|
|
||||||
|
# Console
|
||||||
|
See https://pinout.xyz/pinout/uart for the UART pins. Note that on
|
||||||
|
Linux you need to add `enable_uart=1` to `boot/config.txt`.
|
||||||
|
|
||||||
|
Connect to a USB --> Serial adapter and launch a console at 11500
|
||||||
|
baud.
|
||||||
|
|
||||||
|
# Examples
|
||||||
|
See [dwelch][dwelch]'s bare metal examples. Note that every Pi model
|
||||||
|
has different things on different pins so use the examples from
|
||||||
|
`boards/pizero`.
|
||||||
|
|
||||||
|
[dwelch]: https://github.com/dwelch67/raspberrypi
|
||||||
|
|
||||||
|
# Loading
|
||||||
|
The Pi Zero can boot over USB from a host machine without needing a SD
|
||||||
|
card. This greatly reduces the build/debug cycle. To do this:
|
||||||
|
|
||||||
|
- Fetch `bootcode.bin` and `start.elf` from the [firmware][firmware]
|
||||||
|
repo.
|
||||||
|
- Fetch the [usbboot][usbboot] repo and build.
|
||||||
|
- Copy the `.bin` file to `kernel.img`
|
||||||
|
- Run `sudo rpiboot -d .`
|
||||||
|
- Unplug / plug the board
|
||||||
|
|
||||||
|
A fully worked example is:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ git clone --depth 1 https://github.com/raspberrypi/firmware
|
||||||
|
Cloning into 'firmware'...
|
||||||
|
$ mkdir boot
|
||||||
|
$ cp firmware/boot/bootcode.bin firmware/boot/start.elf boot/
|
||||||
|
|
||||||
|
$ git clone https://github.com/raspberrypi/usbboot.git
|
||||||
|
Cloning into 'usbboot'...
|
||||||
|
$ make -C usbboot/
|
||||||
|
|
||||||
|
$ git clone https://github.com/dwelch67/raspberrypi.git
|
||||||
|
$ make -C raspberrypi/boards/pizero/uart02/
|
||||||
|
$ cp raspberrypi/boards/pizero/uart02/uart02.bin boot/kernel.img
|
||||||
|
|
||||||
|
$ sudo ./usbboot/rpiboot -d boot
|
||||||
|
Waiting for BCM2835/6/7/2711...
|
||||||
|
Sending bootcode.bin
|
||||||
|
Successful read 4 bytes
|
||||||
|
Waiting for BCM2835/6/7/2711...
|
||||||
|
Second stage boot server
|
||||||
|
File read: start.elf
|
||||||
|
File read: kernel.img
|
||||||
|
Second stage boot server done
|
||||||
|
```
|
||||||
|
|
||||||
|
[firmware]: https://github.com/raspberrypi/firmware/tree/master/boot
|
||||||
|
[usbboot]: https://github.com/raspberrypi/usbboot
|
||||||
|
|
||||||
|
# JTAG
|
||||||
|
- Add `enable_jtag_gpio=1` to `boot/config.txt`
|
||||||
|
- Wire up using
|
||||||
|
https://github.com/raspberrypi/linux/issues/1749#issuecomment-265404857
|
||||||
|
- Use the `raspberry.cfg` from
|
||||||
|
https://sysprogs.com/VisualKernel/tutorials/raspberry/jtagsetup/
|
||||||
|
- Run openocd using `openocd -f interface/jlink.cfg -f ./raspberry.cfg`
|
||||||
|
- Run gdb-multiarch against the corresponding ELF file
|
||||||
|
|
||||||
|
giving:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
$ sudo openocd -f interface/jlink.cfg -f ./raspberry.cfg
|
||||||
|
Open On-Chip Debugger 0.10.0
|
||||||
|
Licensed under GNU GPL v2
|
||||||
|
For bug reports, read
|
||||||
|
http://openocd.org/doc/doxygen/bugs.html
|
||||||
|
adapter speed: 100 kHz
|
||||||
|
adapter_nsrst_delay: 400
|
||||||
|
none separate
|
||||||
|
Info : auto-selecting first available session transport "jtag". To override use 'transport select <transport>'.
|
||||||
|
Info : No device selected, using first device.
|
||||||
|
Info : J-Link ARM V8 compiled Nov 28 2014 13:44:46
|
||||||
|
Info : Hardware version: 8.00
|
||||||
|
Info : VTarget = 3.287 V
|
||||||
|
Info : clock speed 100 kHz
|
||||||
|
Info : JTAG tap: rspi.arm tap/device found: 0x07b7617f (mfg: 0x0bf (Broadcom), part: 0x7b76, ver: 0x0)
|
||||||
|
Info : found ARM1176
|
||||||
|
Info : rspi.arm: hardware has 6 breakpoints, 2 watchpoints
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that the ARM CPU needs to be running some code for the JTAG to connect.
|
1
content/post/2020/solarch/featured.jpg
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
mounted.jpg
|
27
content/post/2020/solarch/index.md
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
---
|
||||||
|
date: "2020-11-19T00:00:00+02:00"
|
||||||
|
title: "Solar at home"
|
||||||
|
highlight: true
|
||||||
|
draft: false
|
||||||
|
projects: ["solarch"]
|
||||||
|
---
|
||||||
|
|
||||||
|
For the last month or so I've been working on making my home server
|
||||||
|
(which also hosts this blog!) solar powered. It's pointless but
|
||||||
|
entertaining, and this is a first of a series of posts about what I
|
||||||
|
learned on the way.
|
||||||
|
|
||||||
|
![Solar panel mounted on the frame](mounted.jpg)
|
||||||
|
|
||||||
|
The summary is that a 55 W panel with 200 Wh of battery is enough to
|
||||||
|
power a Raspberry Pi 4 with SSD during the European winter. You still
|
||||||
|
need a mains power fallback, as a couple of days of grey weather will
|
||||||
|
deplete the battery.
|
||||||
|
|
||||||
|
The last 7d have been fairly sunny:
|
||||||
|
|
||||||
|
![Total power generation in Wh per day](totals.png)
|
||||||
|
|
||||||
|
This graph shows the power generated over a 16h window, so the peaks
|
||||||
|
are the total generation for that day. Total for the week was 741 Wh
|
||||||
|
or $0.15.
|
BIN
content/post/2020/solarch/mounted.jpg
Normal file
After Width: | Height: | Size: 102 KiB |
BIN
content/post/2020/solarch/totals.png
Normal file
After Width: | Height: | Size: 43 KiB |
33
content/post/2020/tracking.md
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
+++
|
||||||
|
date = "2020-04-19T00:00:00+02:00"
|
||||||
|
title = "Tracking using a camera"
|
||||||
|
highlight = true
|
||||||
|
draft = false
|
||||||
|
tags = ["kieferbot", "ros"]
|
||||||
|
+++
|
||||||
|
|
||||||
|
Here's Kieferbot being tracked through a camera:
|
||||||
|
|
||||||
|
<video controls src="tracking.mp4"></video>
|
||||||
|
|
||||||
|
It took a few iterations but I settled on matching the colour of the
|
||||||
|
floor to create a floor mask, then [findCountours][fc] to find the
|
||||||
|
contours as a tree, and then searching the children of the root
|
||||||
|
contours. As the bot is black, the children were matched against a
|
||||||
|
'black pixels' mask, and the contour with the most black pixels
|
||||||
|
picked.
|
||||||
|
|
||||||
|
The camera is an Android phone running [IP Webcam][ipw] and sharing
|
||||||
|
the back camera as MJPEG over HTTP over Wifi at 10 FPS. It could be
|
||||||
|
higher, but the laptop doing the image processing has trouble keeping
|
||||||
|
up with 30 FPS. The latency looks good - visually less than 200 ms.
|
||||||
|
|
||||||
|
The tracking seems reliable. If the bot goes off the bottom of the
|
||||||
|
frame then other black areas are picked, but this can be detected by
|
||||||
|
checking for a jump in position.
|
||||||
|
|
||||||
|
Next step is to integrate the camera position, phone sensors, and
|
||||||
|
wheel velocity to get a controllable position.
|
||||||
|
|
||||||
|
[ipw]: https://play.google.com/store/apps/details?id=com.pas.webcam&hl=en
|
||||||
|
[fc]: https://docs.opencv.org/trunk/d4/d73/tutorial_py_contours_begin.html
|
BIN
content/post/2020/tracking/tracking.mp4
Normal file
BIN
content/post/2020/vedirect/featured.png
Normal file
After Width: | Height: | Size: 178 KiB |
24
content/post/2020/vedirect/index.md
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
---
|
||||||
|
title: "VE.Direct"
|
||||||
|
subtitle: "Parser and exporter for the Victron VE.Direct protocol"
|
||||||
|
summary: ""
|
||||||
|
authors: ['michaelh']
|
||||||
|
tags: []
|
||||||
|
categories: []
|
||||||
|
date: 2020-12-01T19:44:20+01:00
|
||||||
|
lastmod: 2020-12-01T19:44:20+01:00
|
||||||
|
featured: false
|
||||||
|
draft: false
|
||||||
|
projects: ['solarch']
|
||||||
|
---
|
||||||
|
|
||||||
|
I've released [vedirect](https://github.com/nzmichaelh/vedirect/), a
|
||||||
|
tool parses the Victron VE.Direct TEXT protocol and exports the
|
||||||
|
metrics over Prometheus and MQTT. This can be used to monitor your
|
||||||
|
solar installation and view statistics in tools like Grafana or Home
|
||||||
|
Assistant.
|
||||||
|
|
||||||
|
See the
|
||||||
|
[README](https://github.com/nzmichaelh/vedirect/blob/master/README.md)
|
||||||
|
for more. I'm using this to monitor the solar system used to power my
|
||||||
|
home server.
|
11
content/post/2020/vouch.md
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
+++
|
||||||
|
date = "2020-10-09T00:02:42+02:00"
|
||||||
|
title = "Vouch for authentication"
|
||||||
|
highlight = true
|
||||||
|
draft = false
|
||||||
|
+++
|
||||||
|
|
||||||
|
I've started using [Vouch](https://github.com/vouch/vouch-proxy) for
|
||||||
|
SSO to my personal site. Quite happy with the experience - it
|
||||||
|
integrates well with Nginx, was straight forward to setup, and is
|
||||||
|
straight forward to enable for different paths on the site.
|
BIN
content/post/2021/abrp/dash.png
Normal file
After Width: | Height: | Size: 120 KiB |
1
content/post/2021/abrp/featured.png
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
panel.png
|
94
content/post/2021/abrp/index.md
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
---
|
||||||
|
# Documentation: https://sourcethemes.com/academic/docs/managing-content/
|
||||||
|
|
||||||
|
title: "VW to ABRP via Home Assistant"
|
||||||
|
authors: ["michaelh"]
|
||||||
|
date: 2021-12-30T19:40:05+01:00
|
||||||
|
lastmod: 2021-12-30T19:40:05+01:00
|
||||||
|
featured: false
|
||||||
|
draft: false
|
||||||
|
|
||||||
|
---
|
||||||
|
I have a Volkswagen ID series car that I'm pretty happy with. The car automatically uploads telemetry such as the current battery state of charge and, with a bit of work, this can be pulled into [Home Assistant](https://www.home-assistant.io/) for viewing and then re-exported to [A Better Route Planner](https://abetterrouteplanner.com/) to give live range planning.
|
||||||
|
|
||||||
|
First, add [ha_vwid](https://github.com/raymondd78/ha_vwid) as a custom component to your Home Assistant installation. I did this by:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cd /data/hass/third_party
|
||||||
|
git clone https://github.com/raymondd78/ha_vwid
|
||||||
|
cd ../custom_components
|
||||||
|
ln -s ../third_party/ha_vwid/custom_components/vwid
|
||||||
|
systemctl restart hass
|
||||||
|
```
|
||||||
|
|
||||||
|
The data had steps in it that looked like VW was throttling my queries, so I reduced the poll rate as well:
|
||||||
|
|
||||||
|
```patch
|
||||||
|
--- a/custom_components/vwid/sensor.py
|
||||||
|
+++ b/custom_components/vwid/sensor.py
|
||||||
|
@@ -80,7 +80,7 @@ async def async_setup_entry(
|
||||||
|
_LOGGER,
|
||||||
|
name = "VW ID Sensor",
|
||||||
|
update_method = async_update_data,
|
||||||
|
- update_interval = timedelta(seconds=30),
|
||||||
|
+ update_interval = timedelta(seconds=121),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Fetch initial data so we have data when entities subscribe
|
||||||
|
```
|
||||||
|
|
||||||
|
Next, go to the Home Assistant UI and enable the `Volkswagen ID` integration. You'll need your We Connect ID user name, password, and VIN. Once done you can add a panel showing the vehicle highlights:
|
||||||
|
|
||||||
|
![Home Assistant panel showing the vehicle data](panel.png)
|
||||||
|
|
||||||
|
Now that Home Assistant has the data, create a command that can post the data to the [Iternio Telemetry API](https://documenter.getpostman.com/view/7396339/SWTK5a8w):
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
rest_command:
|
||||||
|
update_abrp:
|
||||||
|
method: POST
|
||||||
|
headers:
|
||||||
|
authorization: !secret abrp_api_key
|
||||||
|
content_type: "charset=utf-8; application/x-www-form-urlencoded"
|
||||||
|
url: >
|
||||||
|
{% set tlm = {
|
||||||
|
"utc": float(as_timestamp(utcnow())),
|
||||||
|
"soc": states('sensor.volkswagen_id_state_of_charge'),
|
||||||
|
"est_battery_range": float(states('sensor.volkswagen_id_current_range_in_km')),
|
||||||
|
"is_charging": int(states('sensor.volkswagen_id_charge_rate')) > 0,
|
||||||
|
} -%}
|
||||||
|
https://api.iternio.com/1/tlm/send?token={{token}}&tlm={{tlm|to_json|urlencode}}
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that this takes the telemetry, encodes it as JSON, then posts it as a query parameter. You can add other types to the `tlm` dict.
|
||||||
|
|
||||||
|
See the API documentation on how to obtain an API key. Once you have this, add it to `secrets.yaml` like `abrp_api_key: APIKEY 12341234-1234-1234-1234-12312341234`.
|
||||||
|
|
||||||
|
Finally, log into ABRP and use the `ABRP OBD Connection > Link generic` section to get a token for your vehicle. Use this to create an automation rule that runs the REST command whenever the range changes or time passes:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- alias: Push car to ABRP
|
||||||
|
trigger:
|
||||||
|
- platform: state
|
||||||
|
entity_id: sensor.volkswagen_id_current_range_in_km
|
||||||
|
to:
|
||||||
|
- platform: time_pattern
|
||||||
|
minutes: "/8"
|
||||||
|
action:
|
||||||
|
- service: rest_command.update_abrp
|
||||||
|
data:
|
||||||
|
token: the-token-you-get-from-the-link-generic-ui-in-abrp
|
||||||
|
```
|
||||||
|
|
||||||
|
As a bonus, if you enable the Home Assistant Prometheus integration then you can also graph the charging and time remaining:
|
||||||
|
|
||||||
|
![Grafana dashboard showing state of charge and time remaining](dash.png)
|
||||||
|
|
||||||
|
and then use an expression like
|
||||||
|
|
||||||
|
```
|
||||||
|
predict_linear((homeassistant_sensor_unit_minutes>0)[30m:2m], 0)
|
||||||
|
unless (homeassistant_sensor_unit_minutes==0)
|
||||||
|
```
|
||||||
|
|
||||||
|
to get a finer estimate of when the charge will complete and alert when it's almost done.
|
BIN
content/post/2021/abrp/panel.png
Normal file
After Width: | Height: | Size: 96 KiB |
BIN
content/post/2021/at09/featured.jpg
Normal file
After Width: | Height: | Size: 142 KiB |
29
content/post/2021/at09/index.md
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
---
|
||||||
|
title: "AT-09 BLE to UART notes"
|
||||||
|
authors: ['michaelh']
|
||||||
|
date: 2021-03-14T16:34:46+01:00
|
||||||
|
lastmod: 2021-03-14T16:34:46+01:00
|
||||||
|
|
||||||
|
image:
|
||||||
|
caption: ""
|
||||||
|
focal_point: ""
|
||||||
|
preview_only: false
|
||||||
|
|
||||||
|
projects: ['solarch']
|
||||||
|
---
|
||||||
|
I picked up a couple of AT-09 Bluetooth to UART bridges for my solar at home project. Both the BMS and inverter have different grounds than the rest of the system, so rather than worry about isolating the signals I plan to send them over Bluetooth instead.
|
||||||
|
|
||||||
|
The modules have a few gotchas:
|
||||||
|
|
||||||
|
- It seems to have an intra-character timeout of < 0.1 s. If you type in commands manually then the characters don't arrive fast enough and the module returns `ERROR`, so it's best to write a script instead.
|
||||||
|
- Any mutate command causes a reset which takes ~1 s, so it's best to wait for the module to come back by sending `AT` until you get `OK`.
|
||||||
|
- [This is the best manual I could find](http://denethor.wlu.ca/arduino/MLT-BT05-AT-commands-TRANSLATED.pdf) and [this is the best blog post](https://blog.yavilevich.com/2017/03/mlt-bt05-ble-module-a-clone-of-a-clone/).
|
||||||
|
|
||||||
|
`AT+HELP` gives a list of commands but it's incomplete. For reference, I used the following to configure the module:
|
||||||
|
|
||||||
|
* `AT+BAUD5` - set the baud rate to 19200. 4 is 9600.
|
||||||
|
* `AT+NAMEPhoenix 24/375` - set the display name.
|
||||||
|
* `AT+TYPE1` - require a pin to connect.
|
||||||
|
* `AT+PIN123123` - set the 6 digit pin.
|
||||||
|
|
||||||
|
Note that each line must end with a `\r\n`.
|
1
content/post/2021/ausdauer_assembled/featured.jpg
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../../project/ausdauer/ausdauer1.jpg
|
20
content/post/2021/ausdauer_assembled/index.md
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
---
|
||||||
|
title: "Ausdauer: assembled"
|
||||||
|
summary: "I've almost completed the right hand side, and the left hand side is far enough along that I can assemble the whole rover!"
|
||||||
|
authors: ['michaelh']
|
||||||
|
tags: []
|
||||||
|
categories: []
|
||||||
|
date: 2021-01-02T17:17:27+01:00
|
||||||
|
lastmod: 2021-01-02T17:17:27+01:00
|
||||||
|
featured: false
|
||||||
|
draft: false
|
||||||
|
projects: ['ausdauer']
|
||||||
|
---
|
||||||
|
|
||||||
|
I've almost completed the right hand side, and the left hand side is far enough along that I can assemble the whole rover!
|
||||||
|
|
||||||
|
The remaining steps are:
|
||||||
|
|
||||||
|
1. Epoxy the steering wheel and differential inserts in. I didn't modify the print, and the 3D Jake insets are ~1 mm smaller in diameter.
|
||||||
|
2. Cut the grooves on the left hand side shafts.
|
||||||
|
3. Print the differential.
|
1
content/post/2021/ausdauer_assembled2/featured.jpg
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../../project/ausdauer/assembled2.jpg
|
18
content/post/2021/ausdauer_assembled2/index.md
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
---
|
||||||
|
# Documentation: https://sourcethemes.com/academic/docs/managing-content/
|
||||||
|
|
||||||
|
title: "Ausdauer: mechanical assembly"
|
||||||
|
subtitle: "A build of the Sawppy rover"
|
||||||
|
authors: ['michaelh']
|
||||||
|
categories: []
|
||||||
|
date: 2021-01-05T20:50:15+01:00
|
||||||
|
lastmod: 2021-01-05T20:50:15+01:00
|
||||||
|
featured: false
|
||||||
|
draft: false
|
||||||
|
|
||||||
|
projects: ['ausdauer']
|
||||||
|
---
|
||||||
|
|
||||||
|
And that's the end of the mechanical assembly. The differential went on fine, and the epoxy is strong enough to hold in the insets. There's one last thing - I missed the insets on the end of the differential arm, so those are glued and curing
|
||||||
|
|
||||||
|
Next is the electrical assembly. That'll take a while as each cable will need to be cut and extended.
|
BIN
content/post/2021/brainslug/esp.jpg
Normal file
After Width: | Height: | Size: 106 KiB |
BIN
content/post/2021/brainslug/featured.jpg
Normal file
After Width: | Height: | Size: 88 KiB |
109
content/post/2021/brainslug/index.md
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
---
|
||||||
|
# Documentation: https://sourcethemes.com/academic/docs/managing-content/
|
||||||
|
|
||||||
|
title: "Brainslug"
|
||||||
|
summary: "Adding remote control to a plain amplifier"
|
||||||
|
authors: ['michaelh']
|
||||||
|
date: 2021-01-30T17:06:51+01:00
|
||||||
|
lastmod: 2021-01-30T17:06:51+01:00
|
||||||
|
featured: false
|
||||||
|
draft: false
|
||||||
|
---
|
||||||
|
|
||||||
|
I have a [Marantz][marantz] [PM6006][pm6006] amplifier which sounds great, but it's older tech and doesn't integrate with the rest of my home automation. In particular I'd like to have it follow the TV power state and automatically turn on when the TV turns on and turn off shortly after the TV turns off. This should reduce the overall power consumption as it's a class AB amplifier and draws ~50 W even when idle.
|
||||||
|
|
||||||
|
The solution was to add Wifi based control using [ESPHome][esphome]. The integration was pretty clean as all of the signals needed are on the easily accessible STANDBY board which has:
|
||||||
|
|
||||||
|
- The always on 5 V standby supply
|
||||||
|
- The main power relay
|
||||||
|
- The wired remote
|
||||||
|
|
||||||
|
I used an [Olimex][olimex] [ESP8266][modwifi] board that I had in the spare parts drawer. The Olimex has a few unused pads which meant the support components (a MC1700 regulator, filter capacitor, and diode for the remote control interface) could be soldered to the board without needing an extra stripboard.
|
||||||
|
|
||||||
|
![Olimex ESP8266 board with wires off to the standby board](esp.jpg)
|
||||||
|
|
||||||
|
The signals needed are readily available on the STANDBY board. J8508 is +5 V, J8502 is GND, and the top two pins on N8504 are the `RC5` and `POW_ON` relay drive connections. The schematic for the very similar PM6005 is readily available online.
|
||||||
|
|
||||||
|
`POW_ON` connects to `GPIO15` and `RC5` to `GPIO13` via a diode so the infrared remote will still work. The ESPHome configuration is:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
esphome:
|
||||||
|
name: pm6006
|
||||||
|
comment: PM6006 Amplifier
|
||||||
|
platform: esp8266
|
||||||
|
board: modwifi
|
||||||
|
|
||||||
|
wifi:
|
||||||
|
ssid: "your-ssid-here"
|
||||||
|
password: "your-password-here"
|
||||||
|
ap:
|
||||||
|
ssid: "PM6006 Fallback Hotspot"
|
||||||
|
password: "random-data"
|
||||||
|
power_save_mode: HIGH
|
||||||
|
|
||||||
|
captive_portal:
|
||||||
|
|
||||||
|
logger:
|
||||||
|
|
||||||
|
prometheus:
|
||||||
|
|
||||||
|
api:
|
||||||
|
password: "your-secret-here"
|
||||||
|
|
||||||
|
ota:
|
||||||
|
password: "your-secret-here"
|
||||||
|
|
||||||
|
status_led:
|
||||||
|
pin:
|
||||||
|
number: GPIO1
|
||||||
|
inverted: True
|
||||||
|
|
||||||
|
binary_sensor:
|
||||||
|
- platform: gpio
|
||||||
|
name: "PM6006 power sense"
|
||||||
|
id: pm6006_power_sense
|
||||||
|
pin:
|
||||||
|
number: 15
|
||||||
|
|
||||||
|
remote_transmitter:
|
||||||
|
pin:
|
||||||
|
number: GPIO13
|
||||||
|
inverted: True
|
||||||
|
carrier_duty_percent: 100%
|
||||||
|
|
||||||
|
switch:
|
||||||
|
- platform: template
|
||||||
|
name: "PM6006 power"
|
||||||
|
lambda: |-
|
||||||
|
if (id(pm6006_power_sense).state) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
turn_on_action:
|
||||||
|
remote_transmitter.transmit_rc5:
|
||||||
|
address: 0x10
|
||||||
|
command: 0x0C
|
||||||
|
repeat: 2
|
||||||
|
turn_off_action:
|
||||||
|
remote_transmitter.transmit_rc5:
|
||||||
|
address: 0x10
|
||||||
|
command: 0x0C
|
||||||
|
repeat: 2
|
||||||
|
```
|
||||||
|
|
||||||
|
Once flashed, the devices appears in [Home Assistant][hass] and can be controlled from there. The device appears as a single switch where the state reflects the main power relay state so any external changes (like using the remote control) or lost packets will still show the correct power state.
|
||||||
|
|
||||||
|
I had to patch ESPHome to support the remote transmitter inverted mode. I'll clean the patch up and send it upstream.
|
||||||
|
|
||||||
|
Marantz use the [RC-5][rc5] standard commands so I'll add other commands like volume control and input selection in the future.
|
||||||
|
|
||||||
|
I initially tried using a ESP32 board but the initial current draw was too high and caused the standby supply to collapse. The ESP8266 board draws about 70 mA once connected.
|
||||||
|
|
||||||
|
[marantz]: https://www.marantz.com/
|
||||||
|
[pm6006]: https://www.marantz.com/en-gb/shop/amplifier/pm6006ukspecialedition
|
||||||
|
[olimex]: https://www.olimex.com/
|
||||||
|
[modwifi]: https://www.olimex.com/Products/IoT/ESP8266/MOD-WIFI-ESP8266-DEV/open-source-hardware
|
||||||
|
[hass]: https://www.home-assistant.io/
|
||||||
|
[rc5]: https://en.wikipedia.org/wiki/RC-5
|
||||||
|
[esphome]: https://esphome.io/
|
83
content/post/2021/duckie/index.md
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
---
|
||||||
|
# Documentation: https://sourcethemes.com/academic/docs/managing-content/
|
||||||
|
|
||||||
|
title: "Duckiebot debugging"
|
||||||
|
subtitle: ""
|
||||||
|
summary: ""
|
||||||
|
authors: []
|
||||||
|
tags: []
|
||||||
|
categories: []
|
||||||
|
date: 2021-09-26T10:13:19+02:00
|
||||||
|
lastmod: 2021-09-26T10:13:19+02:00
|
||||||
|
featured: false
|
||||||
|
draft: false
|
||||||
|
|
||||||
|
# Featured image
|
||||||
|
# To use, add an image named `featured.jpg/png` to your page's folder.
|
||||||
|
# Focal points: Smart, Center, TopLeft, Top, TopRight, Left, Right, BottomLeft, Bottom, BottomRight.
|
||||||
|
image:
|
||||||
|
caption: ""
|
||||||
|
focal_point: ""
|
||||||
|
preview_only: false
|
||||||
|
|
||||||
|
# Projects (optional).
|
||||||
|
# Associate this post with one or more of your projects.
|
||||||
|
# Simply enter your project's folder or file name without extension.
|
||||||
|
# E.g. `projects = ["internal-project"]` references `content/project/deep-learning/index.md`.
|
||||||
|
# Otherwise, set `projects = []`.
|
||||||
|
projects: []
|
||||||
|
---
|
||||||
|
I've picked up a Duckietown Duckiebot DB21M. Under the hood it's a Linux SBC running ROS that talks to the HUT I/O board. There's a nice host side set of utilities and Docker based tools that let you interact from a standard laptop.
|
||||||
|
|
||||||
|
So far I can't get the motors to spin. I had problems initially with the camera connector being in backwards, which meant the interface ROS node couldn't start, which dropped all interfaces, but that was found via `docker ps` and `docker logs`.
|
||||||
|
|
||||||
|
I'm not fond of the nylon fixings. They're very easy to strip. The supplied SD card seems to be faulty as my laptop doesn't recognise it, but I had a spare class A1 card that imaged fine. The Duckiebattery is cool - it looks like a standard no-name phone charging battery, but it's running custom firmware that reports the battery state over USB.
|
||||||
|
|
||||||
|
It seems the earlier DB17 used an PCA9685 based HAT from Adafruit, and these were integrated into the Hut?
|
||||||
|
|
||||||
|
The [firmware](https://github.com/duckietown/fw-device-hut/tree/jetson-nano) is ATMEGA based and seems to have I2C based registers that run the I/O.
|
||||||
|
|
||||||
|
Reading the firmware, there is LED 0 (0x40?) Motor 1 (0x60?)
|
||||||
|
|
||||||
|
`i2cdetect` doesn't detect these devices but it's skipping addresses.
|
||||||
|
|
||||||
|
`i2cset -y 1 0x40 0 0x20` gets an ACK.
|
||||||
|
|
||||||
|
The LEDs are on command 6 onwards. I can change the colour of the LEDs using `i2cset ...`.
|
||||||
|
|
||||||
|
So the HUT processor is working. The register layout is more complicated so I'll look at the motor supply.
|
||||||
|
|
||||||
|
The motor is sold by Sparkfun as a DG01D-E with pinout https://cdn.sparkfun.com/assets/learn_tutorials/1/1/7/6/assembly_encoder_pin_map.jpg which is:
|
||||||
|
|
||||||
|
* Motor +
|
||||||
|
* Motor -
|
||||||
|
* Encoder VCC
|
||||||
|
* Encoder A
|
||||||
|
* Encoder B
|
||||||
|
* Encoder GND
|
||||||
|
|
||||||
|
The next-door servo pins shows 4.7 V, and it's likely that's the same supply as the motor driver.
|
||||||
|
|
||||||
|
`rqt_graph` shows a range of nodes including `led_emitter_node` and `right_wheel_encoder_node`.
|
||||||
|
|
||||||
|
The code shows that you can run commands like `rosservice call /ente1/led_emitter_node/set_pattern "pattern_name: {data: CAR_SIGNAL_PRIORITY}"`
|
||||||
|
|
||||||
|
The flashing is intermittent. `dmesg` shows that the board is having issues with I2C:
|
||||||
|
|
||||||
|
```
|
||||||
|
[ 498.097932] tegra-i2c 7000c400.i2c: arb lost in communicate to add 0x40
|
||||||
|
[ 498.104813] tegra-i2c 7000c400.i2c: Un-recovered Arb lost
|
||||||
|
[ 500.587954] tegra-i2c 7000c400.i2c: arb lost in communicate to add 0x40
|
||||||
|
```
|
||||||
|
|
||||||
|
The TOF sensor is working, so it's not that. Seems to be on address 0x29.
|
||||||
|
|
||||||
|
What's at address 0x70?
|
||||||
|
|
||||||
|
The RS datasheet has a parts list at https://docs.rs-online.com/c57a/A700000007343652.pdf. The IMU is a MPU-9250 which is normally address 0x68 or 0x69.
|
||||||
|
|
||||||
|
0x70 may be a TCA9548 I2C multiplexer. Does the front bumper board also contain a multiplexer?
|
||||||
|
|
||||||
|
Or another PCA9685?
|
||||||
|
|
||||||
|
The LEDs seem to stop on a different colour, which is what WS2812 LEDs do when the signal is interrupted. Is the HUT resetting?
|
129
content/post/2021/heyo/index.md
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
---
|
||||||
|
# Documentation: https://sourcethemes.com/academic/docs/managing-content/
|
||||||
|
|
||||||
|
title: "HEYO BMS Notes"
|
||||||
|
authors: ['michaelh']
|
||||||
|
tags: ['solar']
|
||||||
|
date: 2021-02-06T17:00:36+01:00
|
||||||
|
lastmod: 2021-02-06T17:00:36+01:00
|
||||||
|
featured: false
|
||||||
|
draft: false
|
||||||
|
|
||||||
|
# Featured image
|
||||||
|
# To use, add an image named `featured.jpg/png` to your page's folder.
|
||||||
|
# Focal points: Smart, Center, TopLeft, Top, TopRight, Left, Right, BottomLeft, Bottom, BottomRight.
|
||||||
|
image:
|
||||||
|
caption: ""
|
||||||
|
focal_point: ""
|
||||||
|
preview_only: false
|
||||||
|
|
||||||
|
# Projects (optional).
|
||||||
|
# Associate this post with one or more of your projects.
|
||||||
|
# Simply enter your project's folder or file name without extension.
|
||||||
|
# E.g. `projects = ["internal-project"]` references `content/project/deep-learning/index.md`.
|
||||||
|
# Otherwise, set `projects = []`.
|
||||||
|
projects: ['solarch']
|
||||||
|
---
|
||||||
|
|
||||||
|
The HEYO BMS appears to be a DALY clone. My 8S 60A model has a selection of ports - on the top:
|
||||||
|
|
||||||
|
* NTC (3 pin)
|
||||||
|
* UART (6 pin)
|
||||||
|
* Unnamed 5 pin)
|
||||||
|
|
||||||
|
On the bottom:
|
||||||
|
|
||||||
|
* Unnamed (4 pin)
|
||||||
|
* Monitor (3 pin)
|
||||||
|
* Unnamed (2 pin)
|
||||||
|
|
||||||
|
It's likely the 2 pin unnamed port is the wake up switch, and the unnamed 5 pin is a RS 485 connection.
|
||||||
|
|
||||||
|
## UART
|
||||||
|
|
||||||
|
The Bluetooth module connects to the 6 pin UART connector with a 6 pin to USB mini B cable. Despite the connector, the signaling is 3.3 V UART at 9600 N81.
|
||||||
|
|
||||||
|
The pins use a confusing colour set. They are:
|
||||||
|
|
||||||
|
1. GND (black)
|
||||||
|
2. +3.3V (green)
|
||||||
|
3. N/C
|
||||||
|
4. N/C
|
||||||
|
5. TXD (white)
|
||||||
|
6. RXD (red)
|
||||||
|
|
||||||
|
The Bluetooth adapter uses BLE with a custom write service and a read service Example frames seen over the Bluetooth UART are:
|
||||||
|
|
||||||
|
To BMS:
|
||||||
|
|
||||||
|
```
|
||||||
|
d2 03 00 00 00 3e d7 b9 ?????>??
|
||||||
|
d2 03 00 a9 00 20 87 91 ????? ??
|
||||||
|
d2 03 00 00 00 3e d7 b9
|
||||||
|
d2 03 00 a9 00 20 87 91 ?????>??????? ??
|
||||||
|
```
|
||||||
|
|
||||||
|
From BMS:
|
||||||
|
|
||||||
|
```
|
||||||
|
d2 03 40 34 30 31 30 31 32 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 42 4d 53 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 65 13 ??@401012??????????????????????????BMS?????????????????????????????e?
|
||||||
|
|
||||||
|
d2 03 7c 0d 6e 0d 69 0d 59 0d d7 0d 0b 0d 0b 0d 0c 0d 0b 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 3d 00 28 00 28 00 00 00 00 00 00 00 00 00 00 01 0f 75 30 03 e8 0d d7 0d 0b 00 3d 00 3d 00 00 00 fa 00 08 00 01 00 00 00 00 00 01 00 01 0d 46 00 cc 00 00 00 00 00 00 00 00 00 00 0e e1 ??|?n?i?Y???????????????????????????????????????????????????????????=?(?(????????????u0???????=?=?????????????????F??????????????
|
||||||
|
```
|
||||||
|
|
||||||
|
Surprisingly this is not the same as the common [SMART BMS protocol](https://blog.ja-ke.tech/2020/02/07/ltt-power-bms-chinese-protocol.html). The overall framing looks straight forward:
|
||||||
|
|
||||||
|
* uint8 SOF: D2
|
||||||
|
* uint8 ???: 03
|
||||||
|
* uint8 Payload length: (40, 7C)
|
||||||
|
* Payload
|
||||||
|
* uint16 Checksum
|
||||||
|
|
||||||
|
The `0d 6e` and similar are likely big endian uint16 battery voltages in mV.
|
||||||
|
|
||||||
|
Next up I'll dig up a Windows machine and capture the serial data between the PC app and the BMS to see if it's the same.
|
||||||
|
|
||||||
|
## CRC
|
||||||
|
|
||||||
|
[CRC RevEng](https://reveng.sourceforge.io/) shows that the checksum is CRC-16/MODBUS:
|
||||||
|
|
||||||
|
```
|
||||||
|
./reveng -w 16 -l -s d203063132333435364c69 d2035200fa0e10000100080000000000010000000000ff012c012c0ea60ea60b54012c012c00e800e8731d731d784d784d0069006900000000006e006e000000000000000000ff00ff0c8000320000000000aa00285737
|
||||||
|
./reveng: warning: you have only given 2 samples
|
||||||
|
./reveng: warning: to reduce false positives, give 4 or more samples
|
||||||
|
width=16 poly=0x8005 init=0xffff refin=true refout=true xorout=0x0000 check=0x4b37 residue=0x0000 name="CRC-16/MODBUS"
|
||||||
|
```
|
||||||
|
|
||||||
|
# Windows app
|
||||||
|
|
||||||
|
Using Wireshark with USBpcap gives the serial stream from the Windows App to the BMS. A sample:
|
||||||
|
|
||||||
|
```
|
||||||
|
1612695069.430 OUT A5 40 90 08 00 00 00 00 00 00 00 00 7D
|
||||||
|
1612695069.647 IN A5 01 90 08 01 06 01 06 75 3A 00 5A 55
|
||||||
|
1612695069.906 OUT A5 40 91 08 00 00 00 00 00 00 00 00 7E
|
||||||
|
1612695070.143 IN A5 01 91 08 0C F9 02 0C AD 07 00 00 06
|
||||||
|
1612695070.191 OUT A5 40 92 08 00 00 00 00 00 00 00 00 7F
|
||||||
|
1612695070.447 IN A5 01 92 08 3D 01 3D 01 00 00 00 00 BC
|
||||||
|
1612695070.520 OUT A5 40 93 08 00 00 00 00 00 00 00 00 80
|
||||||
|
1612695070.655 IN A5 01 93 08 01 01 01 7E 00 00 09 4E 19
|
||||||
|
1612695070.739 OUT A5 40 94 08 00 00 00 00 00 00 00 00 81
|
||||||
|
1612695070.943 IN A5 01 94 08 08 01 01 00 00 00 00 00 4C
|
||||||
|
1612695071.066 OUT A5 40 95 08 00 00 00 00 00 00 00 00 82
|
||||||
|
1612695071.151 IN A5 01 95 08 01 0C F7 0C FA 0C F7 00 50 A5 01 95 08 02 0C F8 0C AF 0C AF 00 BF A5 01 95 08 03 0C AD 0C AD 0C AF 00 73
|
||||||
|
```
|
||||||
|
|
||||||
|
This is different again:
|
||||||
|
|
||||||
|
* uint8 SOF 0xA5
|
||||||
|
* uint8 node ID? 0x40 out, 0x01 from BMS
|
||||||
|
* uint8 register?
|
||||||
|
* uint8 length 0x08
|
||||||
|
* uint8[] body
|
||||||
|
* uint8 checksum
|
||||||
|
|
||||||
|
The checksum seems to be the sum of the payload.
|
||||||
|
|
||||||
|
Requests come from node `0x40` and always have an empty 8 byte payload.
|
||||||
|
|
||||||
|
Some requests like register `0x95` give multiple responses. `0x95` looks like the cell voltages: `0x01` then 3x 16 bit voltage in mV repeated 3 times.
|
40
content/post/2021/pigpio.md
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
---
|
||||||
|
# Documentation: https://sourcethemes.com/academic/docs/managing-content/
|
||||||
|
|
||||||
|
title: "GPIO and Home Assistant"
|
||||||
|
date: 2021-02-28T17:41:07+01:00
|
||||||
|
lastmod: 2021-02-28T17:41:07+01:00
|
||||||
|
featured: false
|
||||||
|
draft: false
|
||||||
|
|
||||||
|
authors: ['michaelh']
|
||||||
|
projects: ['solarch']
|
||||||
|
---
|
||||||
|
As part of moving my home server to solar power, I need a way of switching over to mains if there's been a few grey days and the battery is getting low. I've attached a relay to the Raspberry Pi's GPIO to switch the charger input from solar to a [MEAN WELL 100 W supply](https://www.meanwell-web.com/en-gb/ac-dc-single-output-enclosed-power-supply-lrs--100--36) but integrating is surprisingly tricky:
|
||||||
|
|
||||||
|
- The [`rpi_gpio`](https://www.home-assistant.io/integrations/rpi_gpio/) module uses RPi.GPIO which isn't 64 bit clean and fails to compile on Ubuntu
|
||||||
|
- The [`remote_rpi_gpio`](https://www.home-assistant.io/integrations/remote_rpi_gpio/) module uses [pigpiod](http://abyz.me.uk/rpi/pigpio/pigpiod.html) which for some reason uses 20 % CPU when idle.
|
||||||
|
|
||||||
|
I settled on [`pi-mqtt-gpio`](https://github.com/flyte/pi-mqtt-gpio), as most of my other custom integrations are over MQTT. The config is:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
mqtt:
|
||||||
|
host: localhost
|
||||||
|
topic_prefix: host/keylime
|
||||||
|
discovery: true
|
||||||
|
|
||||||
|
gpio_modules:
|
||||||
|
- name: gpiod
|
||||||
|
module: gpiod
|
||||||
|
|
||||||
|
digital_outputs:
|
||||||
|
- name: mains_select
|
||||||
|
module: gpiod
|
||||||
|
pin: 17
|
||||||
|
on_payload: "ON"
|
||||||
|
off_payload: "OFF"
|
||||||
|
```
|
||||||
|
|
||||||
|
`discovery` is undocumented but enables Home Assistant [MQTT discovery](https://www.home-assistant.io/docs/mqtt/discovery/), so it's zero extra config.
|
||||||
|
|
||||||
|
This should be usable on non-Pi hosts as well due to using the gpiod module.
|
21
content/post/2021/solarlim/index.pdc
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
---
|
||||||
|
# Documentation: https://sourcethemes.com/academic/docs/managing-content/
|
||||||
|
|
||||||
|
title: "Solar limits"
|
||||||
|
authors: ['michaelh']
|
||||||
|
date: 2021-03-06T12:17:07+01:00
|
||||||
|
lastmod: 2021-03-06T12:17:07+01:00
|
||||||
|
featured: false
|
||||||
|
draft: false
|
||||||
|
|
||||||
|
projects: ['solarch']
|
||||||
|
---
|
||||||
|
Something unexpected with my solar power setup: the cable loss is limiting the power produced by the panel, but it's due to the voltage drop and not the power loss.
|
||||||
|
|
||||||
|
The cable is ~20 m of 1 mm^2^ flex for a total of 0.71 Ω. The peak power point of the panel is 34.3 V and the Victron charge controller needs 30.1 V to charge an 8S LiFePO4 battery.
|
||||||
|
|
||||||
|
This means the maximum current is `I = (34.3 - 30.1) / 0.71` = 5.9 A, which is `34.3 * 5.9` = 202 W generated, 24 W of loss, and 178 W delivered.
|
||||||
|
|
||||||
|
![Power delivered versus generated](sim.png)
|
||||||
|
|
||||||
|
Changing to 2.5 mm^2^ flex will cut the resistance to 0.28 Ω, ups the current to 9.2 A, and deliver 291 W.
|
BIN
content/post/2021/solarlim/sim.png
Normal file
After Width: | Height: | Size: 30 KiB |
31
content/post/2021/solarlim/sim.py
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
import numpy as np
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
|
||||||
|
Vpp = 34.3
|
||||||
|
Vmin = 30.1
|
||||||
|
P = np.arange(1, 315 + 1)
|
||||||
|
|
||||||
|
|
||||||
|
def delivered(r: float, p: float) -> float:
|
||||||
|
i = p / Vpp
|
||||||
|
vdrop = i * r
|
||||||
|
vc = Vpp - vdrop
|
||||||
|
if vc <= Vmin:
|
||||||
|
return 0
|
||||||
|
return vc * i
|
||||||
|
|
||||||
|
|
||||||
|
plt.plot(P, [delivered(0.71, p) for p in P], label='1 mm2')
|
||||||
|
plt.plot(P, [delivered(0.28, p) for p in P], color='r', label='2.5 mm2')
|
||||||
|
plt.plot(P, P, color='g', label='limit')
|
||||||
|
|
||||||
|
plt.xlim(0, max(P))
|
||||||
|
plt.ylim(0, max(P))
|
||||||
|
plt.legend()
|
||||||
|
plt.title('Delivered power')
|
||||||
|
plt.xlabel('Ppv')
|
||||||
|
plt.ylabel('Pdelivered')
|
||||||
|
plt.grid()
|
||||||
|
|
||||||
|
plt.savefig('sim.png', transparent=True, bbox_inches='tight')
|
||||||
|
#plt.show()
|
BIN
content/post/2021/solax/featured.png
Normal file
After Width: | Height: | Size: 47 KiB |
97
content/post/2021/solax/index.md
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
---
|
||||||
|
title: "Local SolaX data"
|
||||||
|
subtitle: ""
|
||||||
|
summary: ""
|
||||||
|
authors: []
|
||||||
|
tags: []
|
||||||
|
categories: []
|
||||||
|
date: 2021-12-05T15:49:08+01:00
|
||||||
|
lastmod: 2021-12-05T15:49:08+01:00
|
||||||
|
featured: false
|
||||||
|
draft: false
|
||||||
|
projects: []
|
||||||
|
---
|
||||||
|
I've recently installed a [SolaX X1 Mini](https://www.solaxpower.com/x1-mini/) grid-tied inverter. This comes with a Wifi adapter and cloud offering where you can view the current system performance. This post covers how to redirect the underlying MQTT connection so you can read data from the inverter without data leaving your network.
|
||||||
|
|
||||||
|
The steps are:
|
||||||
|
|
||||||
|
- Patch Mosquitto to accept packets with a zero packet identifier
|
||||||
|
- Configure Mosquitto to also listen on port 2901
|
||||||
|
- Change your DNS to map `mqtt001.solaxcloud.com` and `mqtt002.solaxcloud.com` to your MQTT broker
|
||||||
|
- Configure Home Assistant or similar to unpack the updates
|
||||||
|
|
||||||
|
The uplink is implemented using an unencrypted MQTT connection to `mqtt001.solaxcloud.com` on port 2901. The inverter fails over to `mqtt002.solaxcloud.com` if the first is unavailable.
|
||||||
|
|
||||||
|
The `PUBLISH` packets sent by the inverter are invalid as the packet identifier is set to zero. This can be worked around by patching [Mosquitto](https://mosquitto.org/) and [removing this check](https://github.com/eclipse/mosquitto/blob/3cbe805e71ac41a2a20cc9b2ea6b3b619f49554a/src/handle_publish.c#L107) from `handle_publish.c`. If you don't do this then Mosquitto will detect a protocol violation and close the connection.
|
||||||
|
|
||||||
|
On connection the inverter subscribes to a set of topics, requests the current time, and reports as 'up'. It then publishes the current state every five minutes. All topics contain the Wifi adapter serial number and all payloads are JSON.
|
||||||
|
|
||||||
|
An example update published on `loc/$WIFI_SERIAL_NUMBER` is:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
'type': 'X1-Boost-Air-Mini',
|
||||||
|
'SN': '$WIFI_SERIAL_NUMBER',
|
||||||
|
'ver': '2.033.20',
|
||||||
|
'Data': [
|
||||||
|
0.2, 0.0, 75.0, 0.0, 0.3, 228.8, 7, 18, 0.0, 1.1,
|
||||||
|
0, 17, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0.0, 0.0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
49.99, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0.0, 0, 0, 0, 0, 0.0, 0, 0, 2,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||||
|
0, 0, 0, 0],
|
||||||
|
'Information': [
|
||||||
|
1.1, 4,
|
||||||
|
'X1-Boost-Air-Mini',
|
||||||
|
'$INVERTER_SERIAL_NUMBER',
|
||||||
|
1, 2.1, 0.0, 1.3, 0.0
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
`Data` contains the readings and these seem to correspond to the [direct API fields](https://github.com/GitHobi/solax/wiki/74_SolaxDirect.pm-Readings) decoded by [Hobi](https://github.com/GitHobi) and [used by](https://github.com/squishykid/solax/blob/master/solax/inverter.py) [squishykid](https://github.com/squishykid/). For example, in the readings above:
|
||||||
|
|
||||||
|
- The panels are delivering 0.2 A at 75 V
|
||||||
|
- The grid is at 228.8 V
|
||||||
|
- The system is delivering 8 W and has output 1.1 kWh over the system lifetime
|
||||||
|
|
||||||
|
These can be decoded in [Home Assistant](https://www.home-assistant.io/) using [MQTT sensor definitions](https://www.home-assistant.io/integrations/sensor.mqtt/) like:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- platform: mqtt
|
||||||
|
name: "solax_power_now"
|
||||||
|
unique_id: "solax_power_now"
|
||||||
|
state_topic: "loc/$WIFI_SERIAL_NUMBER"
|
||||||
|
value_template: "{{ value_json.Data[6] }}"
|
||||||
|
expire_after: 900
|
||||||
|
unit_of_measurement: "W"
|
||||||
|
device_class: power
|
||||||
|
```
|
||||||
|
|
||||||
|
which will show as:
|
||||||
|
|
||||||
|
![Power history](featured.png)
|
||||||
|
|
||||||
|
## Notes
|
||||||
|
|
||||||
|
On connection, the inverter subscribes to:
|
||||||
|
|
||||||
|
* `respsynctime/$WIFI_SERIAL_NUMBER`
|
||||||
|
* `set/$WIFI_SERIAL_NUMBER`
|
||||||
|
* `set/$WIFI_SERIAL_NUMBER/init`
|
||||||
|
* `update/version/$WIFI_SERIAL_NUMBER`
|
||||||
|
* `base/down/$WIFI_SERIAL_NUMBER`
|
||||||
|
|
||||||
|
I suspect the `set` topic is used for remote configuration and `update` is used to update the firmware.
|
||||||
|
|
||||||
|
It then:
|
||||||
|
|
||||||
|
* publishes `reqsynctime/$WIFI_SERIAL_NUMBER`: `{'wifisn': '$WIFI_SERIAL_NUMBER'}`
|
||||||
|
* receives `respsynctime/$WIFI_SERIAL_NUMBER`: `{'month': '12', 'hour': '11', 'year': '2021', 'day': '4', 'taskId': '297868bb24194080bdf749ab26adc3d6', 'minute': '20', 'second': '30'}`
|
||||||
|
* publishes `Synctime/$WIFI_SERIAL_NUMBER` `{'code': '2', 'message': 'setting failed'}`
|
||||||
|
* publishes `base/up/$WIFI_SERIAL_NUMBER`: `{'type': 2, 'wifiSN': '$WIFI_SERIAL_NUMBER', 'domain': 'mqtt002.solaxcloud.com', 'port': 2901}`
|
BIN
content/post/2021/zx0/featured.jpg
Normal file
After Width: | Height: | Size: 125 KiB |
32
content/post/2021/zx0/index.md
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
---
|
||||||
|
title: "ZX0"
|
||||||
|
authors: ['michaelh']
|
||||||
|
date: 2021-06-19T16:34:46+01:00
|
||||||
|
lastmod: 2021-06-19T16:34:46+01:00
|
||||||
|
projects: ['zx0']
|
||||||
|
---
|
||||||
|
ZX0 is my [Sinclar ZX81-like][zx81] hobby computer. The idea is to make something in the ZX81 form factor, use a [Raspberry Pi Zero][pizero] for emulation, and then write some interesting apps that run on the emulator.
|
||||||
|
|
||||||
|
Today I got the prototype keyboard working. This uses the ZX81 40 key layout and uses a SAMD21 to scan the keys and report the LEDs. The SAMD21 is running [Zephyr][zephyr], talks to the Pi over I2C, and emulates the [LM8333][lm8333] key matrix scanner.
|
||||||
|
|
||||||
|
In the future I should be able to use the built-in Linux driver but for now the Pi runs a simple program that sends [SMBus][smbus] commands to read the key FIFO and publishes the keys via [uinput][uinput].
|
||||||
|
|
||||||
|
![Board running an emulator](overall.jpg)
|
||||||
|
|
||||||
|
I found quite a few issues with my schematic:
|
||||||
|
|
||||||
|
- `SDA` and `SCL` were swapped. This was fixed in software.
|
||||||
|
- `RX` and `TX` were swapped. This was fixed by swapping the pins in the cable.
|
||||||
|
- The SAMD21 really needs an interrupt line to wake up the Pi. The work-around is to poll.
|
||||||
|
- The USB connector is too hard to hand solder. I've switched this for a chunkier Adafruit connector for future versions.
|
||||||
|
- The regulator has the wrong footprint and pin order. I couldn't get my first choice and ordered something that I thought was pin compatible, but I was wrong. The work-around is to pull the 3V3 line from the Pi.
|
||||||
|
|
||||||
|
Next step is to create a bootloader for the SAMD21 so I can program it from the Pi. I'm thinking of starting with the [Adafruit UF2 bootloader][uf2] and adapting the SAM-BA / BOSSA protocol to work over I2C.
|
||||||
|
|
||||||
|
[zx81]: https://en.wikipedia.org/wiki/ZX81
|
||||||
|
[pizero]: https://www.raspberrypi.org/products/raspberry-pi-zero/
|
||||||
|
[zephyr]: https://www.zephyrproject.org/
|
||||||
|
[lm8333]: https://www.ti.com/product/LM8333
|
||||||
|
[smbus]: https://en.wikipedia.org/wiki/System_Management_Bus
|
||||||
|
[uinput]: https://www.kernel.org/doc/html/latest/input/uinput.html
|
||||||
|
[uf2]: https://github.com/adafruit/uf2-samdx1
|
BIN
content/post/2021/zx0/overall.jpg
Normal file
After Width: | Height: | Size: 122 KiB |
32
content/post/2022/gitjournal.md
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
---
|
||||||
|
# Documentation: https://sourcethemes.com/academic/docs/managing-content/
|
||||||
|
|
||||||
|
title: "Gitjournal"
|
||||||
|
subtitle: ""
|
||||||
|
summary: ""
|
||||||
|
authors: []
|
||||||
|
tags: []
|
||||||
|
categories: []
|
||||||
|
date: 2022-06-26T12:26:02+02:00
|
||||||
|
lastmod: 2022-06-26T12:26:02+02:00
|
||||||
|
featured: false
|
||||||
|
draft: false
|
||||||
|
|
||||||
|
# Featured image
|
||||||
|
# To use, add an image named `featured.jpg/png` to your page's folder.
|
||||||
|
# Focal points: Smart, Center, TopLeft, Top, TopRight, Left, Right, BottomLeft, Bottom, BottomRight.
|
||||||
|
image:
|
||||||
|
caption: ""
|
||||||
|
focal_point: ""
|
||||||
|
preview_only: false
|
||||||
|
|
||||||
|
# Projects (optional).
|
||||||
|
# Associate this post with one or more of your projects.
|
||||||
|
# Simply enter your project's folder or file name without extension.
|
||||||
|
# E.g. `projects = ["internal-project"]` references `content/project/deep-learning/index.md`.
|
||||||
|
# Otherwise, set `projects = []`.
|
||||||
|
projects: []
|
||||||
|
---
|
||||||
|
[GitJournal](https://gitjournal.io/) is very cool. It's an Android app where you can write notes in Markdown and then sync up to your own Git repository.
|
||||||
|
|
||||||
|
I was using NextCloud social for small private notes but that's been abandoned. This setup is clean, text based, future proof, and allows self hosting. Nice!
|
BIN
content/post/2022/nppilot/figure8.mp4
(Stored with Git LFS)
Normal file
28
content/post/2022/nppilot/index.md
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
---
|
||||||
|
# Documentation: https://sourcethemes.com/academic/docs/managing-content/
|
||||||
|
|
||||||
|
title: "nppilot using Ardupilot"
|
||||||
|
authors: ["michaelh"]
|
||||||
|
date: 2022-01-16T17:44:44+01:00
|
||||||
|
lastmod: 2022-01-16T17:44:44+01:00
|
||||||
|
projects: ["nppilot"]
|
||||||
|
---
|
||||||
|
|
||||||
|
I've been working on nppilot, the "never complete autopilot" project for some time now. The latest iteration uses a [Traxxas Revo 1/16][revo], an [OpenPilot Revolution][oprevo] for I/O, and a Raspberry Pi 3B running [ROS][ros] as the command computer.
|
||||||
|
|
||||||
|
And it works! Here's the car running a figure-8 pattern:
|
||||||
|
|
||||||
|
{{<video src="figure8.mp4" controls="yes" >}}
|
||||||
|
|
||||||
|
It doesn't do much at the moment, but that's more progress than the other reboots.
|
||||||
|
|
||||||
|
Some notes on the setup:
|
||||||
|
|
||||||
|
- I changed the spur gear down to 18T to reduce the top speed, get more speed control, and reduce the number of high speed crashes...
|
||||||
|
- Wifi with a range extender seems fine as a monitoring link
|
||||||
|
- I tried a Pi 4B, but my USB power bank can't supply enough current. This matches [benchmarks][pwr] where the 4B is 2.7 W at idle vs 1.2 W.
|
||||||
|
|
||||||
|
[revo]: https://traxxas.com/products/models/electric/erevo-vxl-116-tsm
|
||||||
|
[oprevo]: https://ardupilot.org/copter/docs/common-openpilot-revo-mini.html
|
||||||
|
[ros]: https://ros.org/
|
||||||
|
[pwr]: https://www.pidramble.com/wiki/benchmarks/power-consumption
|
26
content/post/2023/ch32v003/index.md
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
---
|
||||||
|
title: "CH32V003 and Zephyr"
|
||||||
|
authors: ['michaelh']
|
||||||
|
date: 2023-01-29T11:45:23+01:00
|
||||||
|
lastmod: 2023-01-29T11:45:23+01:00
|
||||||
|
featured: false
|
||||||
|
draft: false
|
||||||
|
projects: ['ch32v003']
|
||||||
|
---
|
||||||
|
# CH32V003 and Zephyr
|
||||||
|
|
||||||
|
I've picked up a CH32V003 development board. Their claim to fame is being a
|
||||||
|
RISC-V 32 bit microcontroller with 16 KiB of flash for $0.10 in
|
||||||
|
quantity. I'd like to port Zephyr to it - there's no good reason as
|
||||||
|
the default Zephyr configuration is too big for 16 KiB of flash and 2
|
||||||
|
KiB of RAM, but stripped down it fits quite well.
|
||||||
|
|
||||||
|
I'll update this post as I go along.
|
||||||
|
|
||||||
|
## GPIOD traps
|
||||||
|
|
||||||
|
Be careful if you configure GPIOD at start up. The reset pin and SWD
|
||||||
|
pin are both on GPIOD, and if you set them to output at startup then
|
||||||
|
the chip can no longer be programmed.
|
||||||
|
|
||||||
|
My solution was to desolder and replace the chip.
|
BIN
content/post/2023/charger/charger.png
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
content/post/2023/charger/featured.jpg
Normal file
After Width: | Height: | Size: 77 KiB |
25
content/post/2023/charger/index.md
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
---
|
||||||
|
# Documentation: https://sourcethemes.com/academic/docs/managing-content/
|
||||||
|
|
||||||
|
title: "XT60 based charger"
|
||||||
|
authors: ["michaelh"]
|
||||||
|
tags: ["hacks"]
|
||||||
|
date: 2023-06-04T17:54:35+02:00
|
||||||
|
lastmod: 2023-06-04T17:54:35+02:00
|
||||||
|
featured: false
|
||||||
|
draft: false
|
||||||
|
---
|
||||||
|
|
||||||
|
[charger](https://juju.nz/src/michaelh/hacks/src/branch/master/charger)
|
||||||
|
is a case for a [no-name car charger](https://www.aliexpress.com/item/1005005144919264.html) that lets you use model aircraft
|
||||||
|
batteries to charge your phone.
|
||||||
|
|
||||||
|
The charger is quite nice. It's panel mount and comes with a QC 3.0
|
||||||
|
port and two USB-C power delivery ports. The PD ports charge my
|
||||||
|
Pixel at more than 12 W.
|
||||||
|
|
||||||
|
I made this as we were going camping and wanted to keep our phones
|
||||||
|
charged. I've got quite a few 3S and 4S model aircraft batteries and
|
||||||
|
this was less wasteful than buying a power bank.
|
||||||
|
|
||||||
|
![Render of the case](charger.png)
|
37
content/post/2023/online.md
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
---
|
||||||
|
title: "Faster posting"
|
||||||
|
date: 2023-12-26
|
||||||
|
---
|
||||||
|
TL;DR: the minimal template is:
|
||||||
|
|
||||||
|
```
|
||||||
|
---
|
||||||
|
title: "Faster posting"
|
||||||
|
date: 2023-12-26
|
||||||
|
---
|
||||||
|
Content with no initial title
|
||||||
|
```
|
||||||
|
|
||||||
|
Details:
|
||||||
|
|
||||||
|
I'm using Hugo, which is a static site generator.
|
||||||
|
|
||||||
|
Looks like it's possible to post from gitea. Let's try...
|
||||||
|
|
||||||
|
Step 1: install drone.io. The site builds, and this file is rendered, but it's not part of the index.
|
||||||
|
|
||||||
|
Step 2: add a minimal header with `draft` and `authors`.
|
||||||
|
|
||||||
|
Step 3: add a title and date.
|
||||||
|
|
||||||
|
Step 4: drone stops updating to the latest?
|
||||||
|
|
||||||
|
Step 5: drop the header
|
||||||
|
|
||||||
|
Step 6: re-add the header. The publish date was in the future.
|
||||||
|
|
||||||
|
Step 7: just the date.
|
||||||
|
|
||||||
|
Step 8: just the title.
|
||||||
|
|
||||||
|
Step 9: re-add the date.
|
54
content/post/2024/ampler.md
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
---
|
||||||
|
title: Ampler eBike Bluetooth protocol
|
||||||
|
authors: [michaelh]
|
||||||
|
date: 2024-05-12
|
||||||
|
---
|
||||||
|
|
||||||
|
I have an [Ampler Stout][stout] eBike which exposes metrics as Bluetooth attributes over [GATT]. I'd
|
||||||
|
like to track the battery level and odomoter, so spent some time and decoded some of the attributes.
|
||||||
|
|
||||||
|
The bike exposes:
|
||||||
|
|
||||||
|
| Characteristic | Example data |
|
||||||
|
| ------------------------------------ | ------------------------------------------------------------------------------- |
|
||||||
|
| 340d1528-a89e-4d4e-9078-06d21096ea36 | 0x5b 0x00 0x00 0x00 0x11 0x02 0x00 0x00 0x04 0x01 0x00 0x00 0x00 0x00 0x00 0x00 |
|
||||||
|
| 340d1529-a89e-4d4e-9078-06d21096ea36 | 0x00 0x00 0x00 0x00 0xdf 0x14 0x00 0x00 0x9c 0x0a 0x90 0x00 |
|
||||||
|
| 340d1530-a89e-4d4e-9078-06d21096ea36 | 0xa0 0x00 0x90 0x01 0x02 0x00 0x06 0x00 0x64 0x00 0xfa 0x00 |
|
||||||
|
| 340d1536-a89e-4d4e-9078-06d21096ea36 | 0x01 |
|
||||||
|
| 340d1538-a89e-4d4e-9078-06d21096ea36 | 0x00 |
|
||||||
|
| 340d1542-a89e-4d4e-9078-06d21096ea36 | 0x58 0x02 |
|
||||||
|
| 340d1544-a89e-4d4e-9078-06d21096ea36 | 0x00 |
|
||||||
|
| 340d1545-a89e-4d4e-9078-06d21096ea36 | 0x0a 0x18 0xe2 0x64 |
|
||||||
|
| 340d1547-a89e-4d4e-9078-06d21096ea36 | 0xff 0x02 0x11 0x00 0x00 0x00 0x03 0x00 |
|
||||||
|
| 340d1548-a89e-4d4e-9078-06d21096ea36 | 0x04 0x00 0x00 0x00 0x00 0x00 0x00 0x00 |
|
||||||
|
| 340d1549-a89e-4d4e-9078-06d21096ea36 | 0x00 0x00 0x00 0x00 |
|
||||||
|
| 340d1550-a89e-4d4e-9078-06d21096ea36 | 0x33 |
|
||||||
|
|
||||||
|
The format appears to pack related values into the same characteristic. Values are little endian and
|
||||||
|
either half words or words. The units seem to be metric such as `m` for range and `m/s` for speed
|
||||||
|
with a power of 10 multiplier, such as the voltage being in decivolts:
|
||||||
|
|
||||||
|
| Characteristic | [Format] | Fields |
|
||||||
|
| ------------------------------------ | -------- | --------------------------------------------- |
|
||||||
|
| 340d1528-a89e-4d4e-9078-06d21096ea36 | HHhH | % charged, voltage (dV), temperature (dºC), ? |
|
||||||
|
| 340d1529-a89e-4d4e-9078-06d21096ea36 | III | ?, speed (mm/s), odometer (dm) |
|
||||||
|
| 340d1536-a89e-4d4e-9078-06d21096ea36 | B | assist level (0, 1, 2) |
|
||||||
|
| 340d1538-a89e-4d4e-9078-06d21096ea36 | ? (bool) | light |
|
||||||
|
| 340d1545-a89e-4d4e-9078-06d21096ea36 | I | ? increases over time 1692538885 |
|
||||||
|
|
||||||
|
The time value isn't clear but might be a POSIX time, i.e. roughly Sunday, 20 August 2023.
|
||||||
|
|
||||||
|
Decoding the example above gives:
|
||||||
|
|
||||||
|
| Characteristic | Fields |
|
||||||
|
| ------------------------------------ | ------------------------------------- |
|
||||||
|
| 340d1528-a89e-4d4e-9078-06d21096ea36 | 91 % charged, 52.9 V, 26.0 ºC, 0 |
|
||||||
|
| 340d1529-a89e-4d4e-9078-06d21096ea36 | 0, 5343 mm/s (19.2 km/h), 943.9900 km |
|
||||||
|
| 340d1536-a89e-4d4e-9078-06d21096ea36 | assist level 1 |
|
||||||
|
| 340d1538-a89e-4d4e-9078-06d21096ea36 | light off |
|
||||||
|
|
||||||
|
Next step is to implement a decoder, export this to Home Assistant, and collect it via Prometheus.
|
||||||
|
|
||||||
|
[format]: https://docs.python.org/3/library/struct.html#format-characters
|
||||||
|
[gatt]: https://learn.adafruit.com/introduction-to-bluetooth-low-energy/gatt
|
||||||
|
[stout]: https://amplerbikes.com/en-CH/e-bikes/stout
|
29
content/post/2024/esp32_sensor.md
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
---
|
||||||
|
title: "ESP32 as a low power sensor"
|
||||||
|
date: 2024-04-20
|
||||||
|
---
|
||||||
|
Notes on using an ESP32 as a low power sensor:
|
||||||
|
|
||||||
|
Battery:
|
||||||
|
|
||||||
|
- I have some 18650's already
|
||||||
|
- Battery holder is 76 x 21 x 19
|
||||||
|
- LiPo drop by about 1 % per month
|
||||||
|
- 3000 mAh at 1 %/month is ~40 uA/h
|
||||||
|
|
||||||
|
ESP32:
|
||||||
|
|
||||||
|
- FireBeetle 2 ESP32-C6 is apparently 20 uA in deep sleep
|
||||||
|
- 60 x 25.4 x 7.3 mm
|
||||||
|
- Takes up to a 6 V panel
|
||||||
|
|
||||||
|
Panel:
|
||||||
|
|
||||||
|
- Could use a solar panel to top up the power
|
||||||
|
- A 5 V 65 mA panel is 68 x 36 mm
|
||||||
|
- Ideally the LiPo should stay cool, but the panel should get sun...
|
||||||
|
|
||||||
|
Box:
|
||||||
|
|
||||||
|
- Maximum of dimensions is 76 x 36 x 19 mm
|
||||||
|
- Purecrea have a small 77 x 52 x 28 mm inside dimension box
|
98
content/post/2024/fp6502.md
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
---
|
||||||
|
title: "Learning FPGAs #1"
|
||||||
|
date: 2024-01-01
|
||||||
|
---
|
||||||
|
I normally work at the system or electronic level, and one thing I've always wanted to learn is FPGAs and the corresponding HDLs.
|
||||||
|
|
||||||
|
To pull this together, I plan to implement the 6502 using Verilog on an OrangeCrab board. The board uses a ECP5-25F and is supported by Yosys.
|
||||||
|
|
||||||
|
## 6502
|
||||||
|
|
||||||
|
I chose the 6502 as it reminds me of the VIC-20 and C128 I used to own, and it has
|
||||||
|
a small, straight forward instruction set.
|
||||||
|
|
||||||
|
According to https://llx.com/Neil/a2/opcodes.html, most instructions have a `aaabbbcc` format, where `aaa` and `cc` determine the opcode and `bbb` is the addressing mode. https://www.pagetable.com/c64ref/6502/ has a nice 3-3-2 view of this.
|
||||||
|
|
||||||
|
The instructions are divided into groups based on `cc`:
|
||||||
|
|
||||||
|
- 00 is group 3
|
||||||
|
- 01 is group 1
|
||||||
|
- 10 is group 2
|
||||||
|
- 11 is reserved
|
||||||
|
|
||||||
|
## Group 1 (c = 01)
|
||||||
|
|
||||||
|
Covers 1/4th of the decode space, where `aaa` is:
|
||||||
|
|
||||||
|
- 000 ORA
|
||||||
|
- 001 AND
|
||||||
|
- 010 EOR
|
||||||
|
- 011 ADC
|
||||||
|
- 100 STA
|
||||||
|
- 101 LDA
|
||||||
|
- 110 CMP
|
||||||
|
- 111 SBC
|
||||||
|
|
||||||
|
and `bbb` is:
|
||||||
|
|
||||||
|
- 000 (zero page,X)
|
||||||
|
- 001 zero page
|
||||||
|
- 010 #immediate
|
||||||
|
- 011 absolute
|
||||||
|
- 100 (zero page),Y
|
||||||
|
- 101 zero page,X
|
||||||
|
- 110 absolute,Y
|
||||||
|
- 111 absolute,X
|
||||||
|
|
||||||
|
## Group 2 (c = 10)
|
||||||
|
|
||||||
|
- 000 ASL
|
||||||
|
- 001 ROL
|
||||||
|
- 010 LSR
|
||||||
|
- 011 ROR
|
||||||
|
- 100 STX
|
||||||
|
- 101 LDX
|
||||||
|
- 110 DEC
|
||||||
|
- 111 INC
|
||||||
|
|
||||||
|
where `bbb` is:
|
||||||
|
|
||||||
|
- 000 #immediate
|
||||||
|
- 001 zero page
|
||||||
|
- 010 accumulator
|
||||||
|
- 011 absolute
|
||||||
|
- 101 zero page,X
|
||||||
|
- 111 absolute,X
|
||||||
|
|
||||||
|
So zero page, absolute, zero page, X, and absolute, X are the same.
|
||||||
|
|
||||||
|
## Group 3 (c = 00)
|
||||||
|
|
||||||
|
- 001 BIT
|
||||||
|
- 010 JMP
|
||||||
|
- 011 JMP (abs)
|
||||||
|
- 100 STY
|
||||||
|
- 101 LDY
|
||||||
|
- 110 CPY
|
||||||
|
- 111 CPX
|
||||||
|
|
||||||
|
where `bbb` is:
|
||||||
|
|
||||||
|
- 000 #immediate
|
||||||
|
- 001 zero page
|
||||||
|
- 011 absolute
|
||||||
|
- 101 zero page,X
|
||||||
|
- 111 absolute,X
|
||||||
|
|
||||||
|
which is the same as group 2 except `accumulator`.
|
||||||
|
|
||||||
|
The conditional branches have the form `xxy 100 00`, i.e. b is 100 and c is 00, and x is:
|
||||||
|
|
||||||
|
- 00 negative
|
||||||
|
- 01 overflow
|
||||||
|
- 10 carry
|
||||||
|
- 11 zero
|
||||||
|
|
||||||
|
which is compared with `y`.
|
||||||
|
|
||||||
|
Next is to implement the ALU.
|
11
content/post/2024/fpgablink.md
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
---
|
||||||
|
title: "FPGA and blinking lights"
|
||||||
|
date: 2024-01-13
|
||||||
|
---
|
||||||
|
Today I got the OrangeCrab `blink` example running on my board, yay!
|
||||||
|
|
||||||
|
My daily driver is a Pixelbook, so I resurected an old laptop and put Debian stable on it so I could have
|
||||||
|
a good set of USB ports. The only niggle is that you need to replug the board to get into the boot loader,
|
||||||
|
but you can add a module to the design that watches the boot button and resets when pressed.
|
||||||
|
|
||||||
|
Overally, I'm happy. Next step is to write some RTL.
|