2024-10-04_18-45
@ -1,6 +1,3 @@
|
||||
const pluginTailwind = require('eleventy-plugin-tailwindcss');
|
||||
const { url } = require('./src/_data/site');
|
||||
|
||||
// ocoge start
|
||||
const md = require('markdown-it')({ html: true });
|
||||
// const EleventyFetch = require("@11ty/eleventy-fetch");
|
||||
@ -16,13 +13,8 @@ const metascraper = require('metascraper')([
|
||||
const got = require('got');
|
||||
// ocoge end
|
||||
|
||||
|
||||
module.exports = (config) => {
|
||||
config.addPlugin(pluginTailwind, {
|
||||
src: 'src/assets/css/*'
|
||||
});
|
||||
|
||||
config.setDataDeepMerge(true);
|
||||
|
||||
config.addPassthroughCopy('src/assets/img/**/*');
|
||||
config.addPassthroughCopy({ 'src/posts/img/**/*': 'assets/img/' });
|
||||
|
||||
@ -44,6 +36,7 @@ module.exports = (config) => {
|
||||
// ocoge start
|
||||
config.addPassthroughCopy({ 'src/assets/oc-css/**/*': 'assets/css/' });
|
||||
config.addPassthroughCopy('src/assets/floatbox/**/*');
|
||||
const { execSync } = require('child_process');
|
||||
|
||||
// Shortcodes
|
||||
|
||||
@ -231,7 +224,7 @@ module.exports = (config) => {
|
||||
return {
|
||||
dir: {
|
||||
input: 'src',
|
||||
output: 'htdocs'
|
||||
output: 'dist'
|
||||
},
|
||||
// pathPrefix: "/subfolder/",
|
||||
templateFormats: ['md', 'njk', 'html'],
|
@ -1,8 +1,8 @@
|
||||
const { minify } = require('terser');
|
||||
|
||||
module.exports = (code) => {
|
||||
module.exports = async (code) => {
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
const minified = minify(code);
|
||||
const minified = await minify(code);
|
||||
|
||||
if (minified.error) {
|
||||
console.error('Terser error: ', minified.error);
|
||||
|
@ -1,5 +1,7 @@
|
||||
const { DateTime } = require('luxon');
|
||||
const dayjs = require('dayjs');
|
||||
const utc = require('dayjs/plugin/utc');
|
||||
dayjs.extend(utc);
|
||||
|
||||
module.exports = (date) => {
|
||||
return DateTime.fromJSDate(date, { zone: 'utc' }).toLocaleString(DateTime.DATE_FULL);
|
||||
return dayjs(date).utc().format('YYYY年M月D日');
|
||||
};
|
||||
|
@ -1,8 +1,8 @@
|
||||
const htmlmin = require('html-minifier');
|
||||
const { minify } = require('html-minifier');
|
||||
|
||||
module.exports = (content, outputPath) => {
|
||||
if (process.env.NODE_ENV === 'production' && outputPath.endsWith('.html')) {
|
||||
return htmlmin.minify(content, {
|
||||
return minify(content, {
|
||||
useShortDoctype: true,
|
||||
removeComments: true,
|
||||
collapseWhitespace: true
|
||||
|
@ -1,3 +1,3 @@
|
||||
[build]
|
||||
publish = "dist"
|
||||
command = "npm run build"
|
||||
publish = "dist"
|
||||
command = "npm run build"
|
19057
package-lock.json
generated
92
package.json
@ -1,45 +1,49 @@
|
||||
{
|
||||
"name": "ocogeclub",
|
||||
"version": "0.0.1",
|
||||
"description": "",
|
||||
"scripts": {
|
||||
"clean": "rimraf dist",
|
||||
"start": "npm run serve",
|
||||
"serve": "npm run clean && cross-env NODE_ENV=development eleventy --serve",
|
||||
"watch": "npm run clean && cross-env NODE_ENV=development eleventy --watch",
|
||||
"build": "npm run clean && cross-env NODE_ENV=production eleventy",
|
||||
"debug": "DEBUG=Eleventy* npm run build"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://git.ocoge.club/ocogeclub/pages.git"
|
||||
},
|
||||
"author": "ocogeclub",
|
||||
"license": "CC BY-SA 4.0",
|
||||
"bugs": {
|
||||
"url": "https://https://git.ocoge.club/ocogeclub/issues"
|
||||
},
|
||||
"homepage": "https://https://git.ocoge.club/ocogeclub#readme",
|
||||
"devDependencies": {
|
||||
"@11ty/eleventy": "^0.11.1",
|
||||
"@tailwindcss/typography": "^0.3.1",
|
||||
"cross-env": "^7.0.3",
|
||||
"eleventy-plugin-tailwindcss": "^0.3.0",
|
||||
"html-minifier": "^4.0.0",
|
||||
"luxon": "^1.24.1",
|
||||
"rimraf": "^3.0.2",
|
||||
"terser": "^4.8.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@11ty/eleventy-fetch": "^3.0.0",
|
||||
"markdown-it": "^12.3.2",
|
||||
"metascraper": "^5.29.3",
|
||||
"metascraper-description": "^5.29.3",
|
||||
"metascraper-image": "^5.29.3",
|
||||
"metascraper-logo": "^5.29.3",
|
||||
"metascraper-logo-favicon": "^5.29.3",
|
||||
"metascraper-publisher": "^5.29.3",
|
||||
"metascraper-title": "^5.29.3",
|
||||
"metascraper-url": "^5.29.3"
|
||||
}
|
||||
}
|
||||
"name": "vredeburg",
|
||||
"version": "2.0.0",
|
||||
"description": "A simple starter project to create a blog using Eleventy and Tailwind CSS",
|
||||
"scripts": {
|
||||
"start": "npm run serve",
|
||||
"watch": "concurrently -c auto npm:css:watch npm:11ty:watch",
|
||||
"serve": "concurrently -c auto npm:css:watch npm:11ty:serve",
|
||||
"build": "npm run css:build && npm run 11ty:build",
|
||||
"debug": "cross-env DEBUG=Eleventy* npm run build",
|
||||
"11ty:watch": "cross-env NODE_ENV=development eleventy --watch --incremental",
|
||||
"11ty:serve": "cross-env NODE_ENV=development eleventy --serve --incremental",
|
||||
"11ty:build": "cross-env NODE_ENV=production eleventy",
|
||||
"css:watch": "tailwindcss -i src/assets/css/main.css -o dist/assets/css/main.css -w",
|
||||
"css:build": "tailwindcss -i src/assets/css/main.css -o dist/assets/css/main.css -m"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/daflh/vredeburg.git"
|
||||
},
|
||||
"author": "Dafiul Haq",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/daflh/vredeburg/issues"
|
||||
},
|
||||
"homepage": "https://github.com/daflh/vredeburg#readme",
|
||||
"devDependencies": {
|
||||
"@11ty/eleventy": "^2.0.0",
|
||||
"@tailwindcss/typography": "^0.5.9",
|
||||
"concurrently": "^7.6.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"dayjs": "^1.11.7",
|
||||
"html-minifier": "^4.0.0",
|
||||
"tailwindcss": "^3.2.4",
|
||||
"terser": "^5.16.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"markdown-it": "^14.1.0",
|
||||
"metascraper": "^5.45.23",
|
||||
"metascraper-description": "^5.45.22",
|
||||
"metascraper-image": "^5.45.22",
|
||||
"metascraper-logo": "^5.45.22",
|
||||
"metascraper-logo-favicon": "^5.45.22",
|
||||
"metascraper-publisher": "^5.45.22",
|
||||
"metascraper-title": "^5.45.22",
|
||||
"metascraper-url": "^5.45.22",
|
||||
"pagefind": "^1.1.1"
|
||||
}
|
||||
}
|
||||
|
@ -5,11 +5,11 @@ permalink: 404.html
|
||||
---
|
||||
|
||||
<div class="flex flex-col md:flex-row items-center">
|
||||
<div class="md:mr-9 text-gray-800">{% include "icons/frown.svg" %}</div>
|
||||
<div class="mt-5 md:mt-0 text-center md:text-left text-gray-800">
|
||||
<div class="md:mr-9 text-slate-800">{% include "icons/frown.svg" %}</div>
|
||||
<div class="mt-5 md:mt-0 text-center md:text-left text-slate-800">
|
||||
<h1 class="text-6xl font-medium mt-2 mb-5">404</h1>
|
||||
<h2 class="text-2xl text-gray-800 font-medium leading-snug mb-3 mt-0">Oops! It seems you are lost</h2>
|
||||
<p class="mb-4 md:mb-3 text-gray-600">The page you're looking for might have been removed had its name changed or is temporarily unavailable.</p>
|
||||
<h2 class="text-2xl text-slate-800 font-medium leading-snug mb-3 mt-0">Oops! It seems you are lost</h2>
|
||||
<p class="mb-4 md:mb-3 text-slate-600">The page you're looking for might have been removed had its name changed or is temporarily unavailable.</p>
|
||||
<a href="{{ '/' | url }}" class="text-teal-600 font-semibold">Return to homepage</a>
|
||||
</div>
|
||||
</div>
|
@ -5,7 +5,8 @@ module.exports = {
|
||||
// your site url without trailing slash
|
||||
url: 'https://ocoge.club',
|
||||
// how many posts you want to show for each page
|
||||
paginate: 6,
|
||||
paginate: 9,
|
||||
// if you want to add disqus to your site
|
||||
// disqusShortname: "ocogeclub"
|
||||
// disqusShortname: true
|
||||
// hashoverNext: true
|
||||
};
|
||||
|
@ -54,7 +54,7 @@
|
||||
<meta name="twitter:site" content="@ocogeclub">
|
||||
</head>
|
||||
|
||||
<body class="flex flex-col h-screen bg-white text-gray-800 break-words">
|
||||
<body class="flex flex-col h-screen bg-white text-slate-800 break-words">
|
||||
{% include "partials/header.njk" %}
|
||||
<main class="mx-7 lg:mx-6 mt-32 flex-grow">
|
||||
{% if layout === "post" %}
|
||||
|
@ -5,22 +5,22 @@ css: ["/assets/css/post.css"]
|
||||
|
||||
<article class="max-w-5xl mx-auto">
|
||||
<header class="mb-14">
|
||||
<h1 class="text-3xl text-center font-bold leading-normal text-gray-900 mt-0 mb-3">{{ title }}</h1>
|
||||
<h1 class="text-3xl text-center font-bold leading-normal text-slate-900 mt-0 mb-3" data-pagefind-body>{{ title }}</h1>
|
||||
<div class="text-center"><i class="fa-regular fa-clock"></i> {% if date_disp %}{{ date_disp | readableDate }}{% else %}{{ page.date | readableDate }}{% endif %}</div>
|
||||
{% if tags %}
|
||||
<div class="mt-3 text-center">
|
||||
{% for tag in tags %}
|
||||
<a href="{{ '/tags/' | url }}{{ tag }}" class="inline-block bg-gray-200 rounded-full px-3 py-1 text-sm font-medium text-gray-700 m-0.5">#{{ tag }}</a>
|
||||
<a href="{{ '/tags/' | url }}{{ tag }}" class="inline-block bg-slate-200 rounded-full px-3 py-1 text-sm font-medium text-slate-700 m-0.5">#{{ tag }}</a>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if thumb %}
|
||||
<div class="mt-10 -mx-7 md:mx-0">
|
||||
<img class="w-full max-w-2xl mx-auto" src="{{ thumb | url }}" alt="This post thumbnail">
|
||||
<img class="w-full max-w-2xl mx-auto" src="{{ thumb | url }}" width="960" height="500" alt="This post thumbnail">
|
||||
</div>
|
||||
{% endif %}
|
||||
</header>
|
||||
<div id="content" class="prose text-gray-800 max-w-none">
|
||||
<div id="content" class="prose text-slate-800 max-w-none" data-pagefind-body>
|
||||
{{ content | safe }}
|
||||
</div>
|
||||
{% if site.disqusShortname %}
|
||||
@ -28,8 +28,8 @@ css: ["/assets/css/post.css"]
|
||||
{% if process.environment === "production" %}
|
||||
{% include "partials/disqus.njk" %}
|
||||
{% else %}
|
||||
<div class="italic text-gray-700">Disqus comments only available for production</div>
|
||||
<div class="italic text-slate-700">Disqus comments only available for production</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</article>
|
||||
</article>
|
@ -1,8 +1,8 @@
|
||||
<footer class="mt-20 px-10 py-8 bg-gray-200">
|
||||
<footer class="mt-20 px-10 py-8 bg-slate-200">
|
||||
<div id="footer-badge"><a href="https://git.ocoge.club/" target=_new><img loading="lazy" src="/assets/img/git-ocogeclub.svg" class="footer-img"></a><a href="http://creativecommons.org/licenses/by-sa/4.0/deed.ja" target=_new><img loading="lazy" src="/assets/img/License_BY-SA_4.0.svg" class="footer-img"></a></div>
|
||||
<div class="max-w-5xl mx-auto text-gray-700 text-center">
|
||||
© {% year %} <a href="{{ '/' | url }}" class="font-medium" target="_blank" rel="noopener">{{ site.title }}</a>.
|
||||
<div class="max-w-5xl mx-auto text-slate-700 text-center">
|
||||
© 2020 <a href="{{ '/' | url }}" class="font-medium" target="_blank" rel="noopener">{{ site.title }}</a>.
|
||||
Made by <a href="{{ author.homepage | url }}" target="_blank" rel="noopener">{{ author.name }}</a>
|
||||
using <a href="https://www.11ty.dev" target="_blank" rel="noopener">Eleventy</a> and <a href="https://github.com/dafiulh/vredeburg" target="_blank" rel="noopener">Vredeburg</a>.<br>
|
||||
using <a href="https://www.11ty.dev" target="_blank" rel="noopener">Eleventy</a> and <a href="https://tailwindcss.com" target="_blank" rel="noopener">Tailwind CSS</a>.
|
||||
</div>
|
||||
</footer>
|
@ -1,4 +1,4 @@
|
||||
<header id="header" class="header-shadow bg-white px-6 py-5 z-50 fixed w-full top-0 transition-all transform ease-in-out duration-500">
|
||||
<header id="header" class="bg-white w-full px-6 py-5 z-50 fixed top-0 shadow-md transition-all transform ease-in-out duration-500">
|
||||
<div class="max-w-5xl mx-auto flex items-center flex-wrap justify-between">
|
||||
<div class="sm:mr-8">
|
||||
<a class="flex items-center" href="{{ '/' | url }}">
|
||||
@ -7,14 +7,10 @@
|
||||
</div>
|
||||
<nav id="menu" class="order-last md:order-none items-center flex-grow w-full md:w-auto md:flex hidden mt-2 md:mt-0">
|
||||
{% for item in menu %}
|
||||
<a href="{{ item.url | url }}" {% if item.newTab %}target="_blank" rel="noopener" {% endif %}class="block mt-4 md:inline-block md:mt-0 font-medium text-gray-700 hover:text-teal-600 text-base mr-4">{{ item.label }}</a>
|
||||
<a href="{{ item.url | url }}" {% if item.newTab %}target="_blank" rel="noopener" {% endif %}class="block mt-4 md:inline-block md:mt-0 font-medium text-slate-700 hover:text-teal-600 text-base mr-4">{{ item.label }}</a>
|
||||
{% endfor %}
|
||||
</nav>
|
||||
<form id="search" action="{{ '/search' | url }}" class="order-last sm:order-none flex-grow items-center justify-end hidden sm:block mt-6 sm:mt-0">
|
||||
<label class="visually-hidden" for="header-searchbox">記事タイトル/概要を検索 ...</label>
|
||||
<input type="text" id="header-searchbox" name="q" placeholder="記事タイトル/概要を検索 ..." class="w-full sm:max-w-xs bg-gray-200 border border-transparent float-right focus:bg-white focus:border-gray-300 focus:outline-none h-8 p-4 placeholder-gray-500 rounded text-gray-700 text-sm">
|
||||
</form>
|
||||
<div id="menu-toggle" class="flex items-center md:hidden text-gray-700 hover:text-teal-600 cursor-pointer sm:ml-6">
|
||||
<div id="menu-toggle" class="flex items-center md:hidden text-slate-700 hover:text-teal-600 cursor-pointer sm:ml-6">
|
||||
{% include "icons/menu.svg" %}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,4 +1,4 @@
|
||||
<div class="mt-3 flow-root">
|
||||
<a href="{% if not paged.first %}{{ pagination.href.previous | url }}{% else %}javascript:void(0){% endif %}" class="float-left bg-white font-semibold py-2 px-4 border rounded shadow-md text-gray-800 {% if not paged.first %}cursor-pointer hover:bg-gray-100{% else %}cursor-default text-opacity-75{% endif %}">Previous</a>
|
||||
<a href="{% if not paged.last %}{{ pagination.href.next | url }}{% else %}javascript:void(0){% endif %}" class="float-right bg-white font-semibold py-2 px-4 border rounded shadow-md text-gray-800 {% if not paged.last %}cursor-pointer hover:bg-gray-100{% else %}cursor-default text-opacity-75{% endif %}">Next</a>
|
||||
<a href="{% if not paged.first %}{{ pagination.href.previous | url }}{% else %}javascript:void(0){% endif %}" class="float-left bg-white font-semibold py-2 px-4 border rounded shadow-md text-slate-800 {% if not paged.first %}cursor-pointer hover:bg-slate-100{% else %}cursor-default text-opacity-50{% endif %}">Previous</a>
|
||||
<a href="{% if not paged.last %}{{ pagination.href.next | url }}{% else %}javascript:void(0){% endif %}" class="float-right bg-white font-semibold py-2 px-4 border rounded shadow-md text-slate-800 {% if not paged.last %}cursor-pointer hover:bg-slate-100{% else %}cursor-default text-opacity-50{% endif %}">Next</a>
|
||||
</div>
|
@ -7,7 +7,7 @@
|
||||
</a>
|
||||
<div class="px-6 py-5">
|
||||
<div class="font-semibold text-lg mb-2">
|
||||
<a class="text-gray-900 hover:text-gray-700" href="{{ post.url | url }}">{{ post.data.title }}</a>
|
||||
<a class="text-slate-900 hover:text-slate-700" href="{{ post.url | url }}">{{ post.data.title }}</a>
|
||||
</div>
|
||||
<p class="text-gray-500 mb-1" title="Published date">{% if post.data.date_disp %}<i class="fa-solid fa-thumbtack" title="Sticky"></i> {{ post.data.date_disp | readableDate }}{% else %}<i class="fa-regular fa-clock"></i> {{ post.data.page.date | readableDate }}{% endif %}</p>
|
||||
<p class="text-gray-600">
|
||||
|
@ -10,21 +10,21 @@ title: About Me
|
||||
</a>
|
||||
{% endif %}
|
||||
<div class="text-center mt-8">
|
||||
<h1 class="text-2xl font-medium text-gray-800 leading-relaxed mt-0 mb-1">
|
||||
<h1 class="text-2xl font-medium text-slate-800 leading-relaxed mt-0 mb-1">
|
||||
<a href="{{ author.homepage | url }}" target="_blank" rel="noopener">{{ author.name }}</a>
|
||||
</h1>
|
||||
{% if author.email %}
|
||||
<div class="text-gray-600">
|
||||
<div class="text-slate-600">
|
||||
<a href="mailto:{{ author.email }}">{{ author.email }}</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if author.phone %}
|
||||
<div class="text-gray-600">
|
||||
<div class="text-slate-600">
|
||||
<a href="tel:{{ author.phone }}">{{ author.phone }}</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if author.bio %}
|
||||
<p class="mt-3 text-gray-700">{{ author.bio }}</p>
|
||||
<p class="mt-3 text-slate-700">{{ author.bio }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
@ -3,20 +3,20 @@
|
||||
@tailwind utilities;
|
||||
|
||||
@layer base {
|
||||
body {
|
||||
font-family: Inter,system-ui,-apple-system,BlinkMacSystemFont,helvetica,Arial,sans-serif;
|
||||
}
|
||||
body {
|
||||
font-family: Inter,system-ui,-apple-system,BlinkMacSystemFont,helvetica,Arial,sans-serif;
|
||||
}
|
||||
}
|
||||
|
||||
@layer utilities {
|
||||
.header-shadow {
|
||||
box-shadow: 0 3px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 5px -1px rgba(0, 0, 0, 0.06)
|
||||
}
|
||||
.header-shadow {
|
||||
box-shadow: 0 3px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 5px -1px rgba(0, 0, 0, 0.06)
|
||||
}
|
||||
|
||||
.visually-hidden {
|
||||
@apply block absolute h-px w-px overflow-hidden whitespace-nowrap;
|
||||
clip: rect(1px 1px 1px 1px);
|
||||
clip: rect(1px, 1px, 1px, 1px);
|
||||
clip-path: inset(1px);
|
||||
}
|
||||
}
|
||||
.visually-hidden {
|
||||
@apply block absolute h-px w-px overflow-hidden whitespace-nowrap;
|
||||
clip: rect(1px 1px 1px 1px);
|
||||
clip: rect(1px, 1px, 1px, 1px);
|
||||
clip-path: inset(1px);
|
||||
}
|
||||
}
|
||||
|
@ -11,11 +11,11 @@ window.addEventListener('scroll', () => {
|
||||
if (currPos > lastPos) {
|
||||
if (currPos > header.offsetHeight) {
|
||||
header.classList.add('-translate-y-full');
|
||||
header.classList.remove('header-shadow');
|
||||
header.classList.remove('shadow-md');
|
||||
}
|
||||
} else {
|
||||
header.classList.remove('-translate-y-full');
|
||||
header.classList.add('header-shadow');
|
||||
header.classList.add('shadow-md');
|
||||
}
|
||||
|
||||
lastPos = currPos;
|
||||
|
@ -1,114 +0,0 @@
|
||||
/*
|
||||
Search for posts with keyword given in the parameter "q"
|
||||
Only run on search page ("/search/")
|
||||
*/
|
||||
|
||||
class SearchPosts {
|
||||
async init() {
|
||||
const params = new URL(location.href).searchParams;
|
||||
|
||||
this.start = Number(params.get('start')) || 1;
|
||||
this.size = Number(params.get('size')) || 12;
|
||||
|
||||
this.posts = await fetch('../index.json').then((res) => {
|
||||
return res.json();
|
||||
});
|
||||
|
||||
this.render(params.get('q'));
|
||||
}
|
||||
|
||||
render(query) {
|
||||
const wrapperEl = document.getElementById('wrapper');
|
||||
const searchBoxEl = document.getElementById('searchbox');
|
||||
const infoEl = document.getElementById('info');
|
||||
|
||||
query = typeof query === 'string' ? query.toLowerCase() : '';
|
||||
|
||||
history.replaceState(null, null, `?q=${query}&start=${this.start}&size=${this.size}`);
|
||||
|
||||
searchBoxEl.value = query;
|
||||
wrapperEl.innerHTML = '';
|
||||
|
||||
if (query === '') {
|
||||
infoEl.textContent = 'Enter keywords in the search box above';
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const matchedPosts = this.posts.filter((post) => {
|
||||
const postTitle = post.title.toLowerCase();
|
||||
const postDescription = post.description.toLowerCase();
|
||||
|
||||
return (postTitle.indexOf(query) !== -1) || (postDescription.indexOf(query) !== -1);
|
||||
});
|
||||
|
||||
if (matchedPosts.length === 0) {
|
||||
infoEl.textContent = `No results were found for "${query}"`;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const size = this.size;
|
||||
const offset = this.start - 1;
|
||||
const slicedPosts = matchedPosts.slice(offset, offset + size);
|
||||
|
||||
const lastPostIndex = offset + slicedPosts.length;
|
||||
const showingRange = this.start < lastPostIndex || this.start !== 1 ? `${this.start} to ${lastPostIndex}` : this.start;
|
||||
const extraS = matchedPosts.length > 1 ? 's' : '';
|
||||
|
||||
infoEl.textContent = `Showing ${showingRange} of ${matchedPosts.length} result${extraS} found for "${query}"`;
|
||||
|
||||
slicedPosts.forEach((post) => {
|
||||
const { url, title, date, description } = post;
|
||||
|
||||
wrapperEl.innerHTML += `
|
||||
<div class="w-full sm:w-1/2 md:w-1/3 self-stretch p-2 mb-2">
|
||||
<a href="${url}">
|
||||
<div class="rounded shadow-md h-full px-6 py-5">
|
||||
<div class="font-semibold text-lg mb-2">${title}</div>
|
||||
<p class="text-gray-500 mb-1 text-sm" title="Published date">${date}</p>
|
||||
<p class="text-gray-600 text-sm">${description}</p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (location.pathname === '/search/') {
|
||||
const searchBoxEl = document.getElementById('searchbox');
|
||||
const searchPosts = new SearchPosts();
|
||||
|
||||
searchPosts.init();
|
||||
|
||||
searchBoxEl.addEventListener('keyup', debounce(function () {
|
||||
searchPosts.render(this.value);
|
||||
}, 400));
|
||||
}
|
||||
|
||||
// https://github.com/sindresorhus/p-debounce
|
||||
function debounce(fn, wait) {
|
||||
let timer;
|
||||
let resolveList = [];
|
||||
|
||||
return function (...arguments_) {
|
||||
return new Promise((resolve) => {
|
||||
clearTimeout(timer);
|
||||
|
||||
timer = setTimeout(() => {
|
||||
timer = null;
|
||||
|
||||
const result = fn.apply(this, arguments_);
|
||||
|
||||
for (resolve of resolveList) {
|
||||
resolve(result);
|
||||
}
|
||||
|
||||
resolveList = [];
|
||||
}, wait);
|
||||
|
||||
resolveList.push(resolve);
|
||||
});
|
||||
};
|
||||
}
|
@ -3,6 +3,5 @@ permalink: "/assets/js/bundle.js"
|
||||
---
|
||||
{% set js %}
|
||||
{% include "../assets/js/main.js" %}
|
||||
{% include "../assets/js/search.js" %}
|
||||
{% endset %}
|
||||
{{ js | minifyJs | safe }}
|
@ -1,13 +0,0 @@
|
||||
---
|
||||
permalink: "/index.json"
|
||||
---
|
||||
[
|
||||
{% for post in collections.posts %}
|
||||
{
|
||||
"url": "{{ post.url | url }}",
|
||||
"title": "{{ post.data.title }}",
|
||||
"date": "{{ post.data.page.date | readableDate }}",
|
||||
"description": "{{ post.data.description }}"
|
||||
}{% if not loop.last %},{% endif %}
|
||||
{% endfor %}
|
||||
]
|
@ -1,7 +1,8 @@
|
||||
---
|
||||
title: "『情報コース』もっとくわしく編"
|
||||
description: "『情報コース』では Raspberry Pi やブロックプログラム開発環境『オコゲ』、センサやモータなどを使用して、楽しみながら ICT や IoT などの情報技術を学習します。"
|
||||
date: 2022-04-27
|
||||
date: 2199-12-30
|
||||
date_disp: 2022-04-27
|
||||
thumb: "ogp_tama-graphig.webp"
|
||||
tags:
|
||||
- ICT-Course
|
||||
|
@ -1,7 +1,8 @@
|
||||
---
|
||||
title: "ブロックプログラミング環境『オコゲ』"
|
||||
description: "『オコゲ』は、Google Blockly ライブラリを使用して作られた、Raspberry Pi 向けブロックプログラミング環境です。"
|
||||
date: 2022-05-01
|
||||
date: 2199-12-29
|
||||
date_disp: 2022-05-01
|
||||
thumb: "ogp_ocoge.webp"
|
||||
tags: オコゲ
|
||||
---
|
||||
|
@ -1,7 +1,8 @@
|
||||
---
|
||||
title: "『オコゲ』Web 体験版"
|
||||
description: "ブロックプログラミングを体験してみましょう。ハードウェアなどの制限から、Web体験版ではできないこともありますが、感触を確かめることはできます。"
|
||||
date: 2022-05-07
|
||||
date: 2199-12-25
|
||||
date_disp: 2022-05-07
|
||||
thumb: "raspberrycake_ogp.webp"
|
||||
tags:
|
||||
- ICT-Course
|
||||
|
@ -1,68 +0,0 @@
|
||||
---
|
||||
eleventyExcludeFromCollections: true
|
||||
title: "おこげ倶楽部自宅サーバについて"
|
||||
description: "ocoge.club は Orange Pi 自宅サーバにてホスティングされています。Webサーバ、メールサーバ、Git サーバ稼働中。"
|
||||
date: 2022-05-07
|
||||
thumb: "opi-server.webp"
|
||||
tags:
|
||||
- 技術情報
|
||||
---
|
||||
|
||||
ocoge.club は Orange Pi 自宅サーバ(上写真)にてホスティングされています。現在 Webサーバ、メールサーバ、Git サーバが稼働しています。数千円で購入できる手のひらサイズのシングルボードコンピュータと3千円程度のSSDを使ってなかなか充実したネットサービスごっこwができます。
|
||||
|
||||
{% talkicon_l {img: 'tama.webp', name: 'タマちー'} %}{% says_l %}
|
||||
「おこげ俱楽部」は、軽量多目的 ~~Raspberry Pi~~ {% link_new {uri: 'http://www.orangepi.org/',title: 'Orange Pi'} %}Orange Pi{% endlink_new %} サーバーとして絶賛改装中です...。
|
||||
{% endsays_l %}
|
||||
{% talkicon_r {img: 'mimmy.webp', name: 'ミミィ'} %}{% says_r %}
|
||||
市場でラズパイが枯渇している関係で、サーバのハードウェアを 4B(4GB) から、現状最も手に入りやすい中華~~パチモン~~ <span title="シングルボードコンピュータ">SBC</span> の Orange Pi に変更したのよね!軽量化のために{% span "mfy" %}{% link_new {uri: "https://ja.wordpress.org", title: "シェア世界一の CMS"} %}WordPress{% endlink_new %}{% endspan %} をやめて {% span "mfg" %}{% link_new {uri: "https://jamstack.org", title: "ちょっと何言ってるか分からない"} %}Jamstack{% endlink_new %}{% endspan %} にするとかの対応中ヨ!
|
||||
{% endsays_r %}
|
||||
{% talkicon_l {img: 'tama.webp', name: ''} %}{% think_l %}
|
||||
らずぱい売ってない... 困る...
|
||||
{% endthink_l %}
|
||||
{% talkicon_r {img: 'mimmy.webp', name: ''} %}{% says_r %}
|
||||
RSコンポーネンツの通販サイトに「11月入荷」とか書いてあるのよ!
|
||||
鬼も笑えないわ!!
|
||||
{% endsays_r %}
|
||||
|
||||
## ハードウェア構成
|
||||
|
||||
- {% link_new {uri: 'http://www.orangepi.org/Orange%20Pi%203%20LTS/index.html',title: ''} %}Orange Pi 3 LTS{% endlink_new %}
|
||||
- Crucial SSD 120GB 2.5インチ (CT120BX500SSD1JP)
|
||||
- Salcar USB-C 2.5インチ SATA HDD/SSDケース
|
||||
|
||||
ケースは RPi 用を流用。ネジ穴が合わなかったので百均の耐震マットを小さく切って貼り付け。
|
||||
|
||||
## ソフトウェア構成
|
||||
|
||||
| 種類 | 名称 |
|
||||
| --- | --- |
|
||||
| OS | {% link_new {uri: 'http://www.orangepi.org/downloadresources/',title: ''} %}Orange Pi3 LTS Debian Buster Server{% endlink_new %} |
|
||||
| Webサーバ | {% link_new {uri: 'https://nginx.org/en/',title: ''} %}Nginx{% endlink_new %} |
|
||||
| サーバサイドインタプリタ | {% link_new {uri: 'https://www.php.net/',title: ''} %}PHP{% endlink_new %} |
|
||||
| データベース | {% link_new {uri: 'https://www.sqlite.org/index.html',title: ''} %}SQLite3{% endlink_new %} |
|
||||
| メールサーバ (MTA) | {% link_new {uri: 'http://www.postfix.org/',title: ''} %}Postfix{% endlink_new %} |
|
||||
| Webメールクライアント | {% link_new {uri: 'https://www.rainloop.net/',title: ''} %}Rainloop{% endlink_new %} |
|
||||
| POP/IMAP サーバ | {% link_new {uri: 'https://www.dovecot.org/',title: ''} %}Dovecot{% endlink_new %} |
|
||||
| Git サーバ | {% link_new {uri: 'https://gitea.io/',title: ''} %}Gitea{% endlink_new %} |
|
||||
|
||||
このほか、スパムメール対策に {% link_new {uri: 'https://spamassassin.apache.org/',title: ''} %}SpamAssasin{% endlink_new %} であるとか証明書取得に {% link_new {uri: 'https://letsencrypt.org/ja/',title: ''} %}Let’s Encrypt (certbot){% endlink_new %} であるとか
|
||||
|
||||
## その他利用しているもの
|
||||
|
||||
独自ドメイン (ocoge.club)
|
||||
{% link_new {uri: 'https://www.mydns.jp/',title: ''} %}MyDNS{% endlink_new %}(無料ダイナミックDNS)
|
||||
インターネット接続回線(有線LANで接続)
|
||||
静的サイトジェネレータ {% link_new {uri: 'https://www.11ty.dev/',title: ''} %}11ty{% endlink_new %} 及びブログテーマ {% link_new {uri: 'https://github.com/dafiulh/vredeburg',title: ''} %}Vredeburg{% endlink_new %}
|
||||
|
||||
## 参考サイト
|
||||
|
||||
{% link_new {uri: 'https://samhobbs.co.uk/raspberry-pi-email-server',title: ''} %}Sam Hobbs | Raspberry Pi Email Server{% endlink_new %}
|
||||
英語でメールサーバのみの解説ではあるが、ステップごとにインストールや設定がうまくいっているかどうか確認を行うことができるようになっているため、長い作業の中でどこでミスをしたのか、または不具合が出ているのかがわかりやすい。また、設定をデフォルトから変更する箇所にそれぞれ設定の意味の説明があり、自分が何をしているのかがわかるため、ソフトウェアのアップデートで設定項目やその値に変更があり見本と齟齬が生じていても対処が容易である。わけもわからず言われた通り延々ソフトウェアのインストールと設定ファイルの書き換えを繰り返し、辟易しながら終わらせたものの結局動作せず問題のある個所も見当がつかないまま挫折、などという悲劇を起こしにくい、ナイスなサイト。
|
||||
|
||||
{% talkicon_l {img: 'tama.webp', name: ''} %}{% says_l %}
|
||||
WordPress と NextCloud の運用をやめた理由は実は別にあったりする...
|
||||
{% endsays_l %}
|
||||
{% talkicon_r {img: 'mimmy.webp', name: ''} %}{% says_r %}
|
||||
セキュリティアップデートが面倒だったのヨ!
|
||||
仕事でもないのにあんなのやってられないワ!!
|
||||
{% endsays_r %}
|
@ -1,7 +1,8 @@
|
||||
---
|
||||
title: "受講生限定アカウントサービス"
|
||||
description: "『情報コース』受講者でご希望の方に、『おこげ倶楽部』オリジナルのメールアドレスとクラウドストレージを進呈しております。"
|
||||
date: 2022-05-13
|
||||
date: 2199-12-28
|
||||
date_disp: 2022-05-13
|
||||
thumb: "ogp_ocogeclub-duck.webp"
|
||||
tags:
|
||||
- ICT-Course
|
||||
|
@ -1,7 +1,8 @@
|
||||
---
|
||||
title: "『オコゲ』マスコットのご紹介"
|
||||
description: "『オコゲ』のキャンバスにデフォルトで表示されるマスコットキャラクターをご紹介。"
|
||||
date: 2022-05-13
|
||||
date: 2199-12-26
|
||||
date_disp: 2022-05-13
|
||||
thumb: "ogp_tama-and-mimmy.webp"
|
||||
tags:
|
||||
- オコゲ
|
||||
|
@ -1,7 +1,8 @@
|
||||
---
|
||||
title: "プチレッスン『タイプライターっぽいもの』その1"
|
||||
description: "サンプルプログラム「タイプライターもどき」をもうちょっとだけタイプライターっぽくしてみましょう。"
|
||||
date: 2022-05-15
|
||||
date: 2199-12-23
|
||||
date_disp:: 2022-05-15
|
||||
thumb: "ogp_typewriter.webp"
|
||||
tags:
|
||||
- オコゲ
|
||||
|
@ -1,175 +0,0 @@
|
||||
---
|
||||
title: "『オコゲ』でぐーちょきぱーを機械学習"
|
||||
description: "『オコゲ』デスクトップ版の機能を動画でご紹介。サーマルカメラと TensorFlow を使ったインタラクティブでリアルタイムな機械学習。"
|
||||
date: 2022-05-16
|
||||
thumb: "ogp_janken.webp"
|
||||
tags:
|
||||
- ICT-Course
|
||||
- 動画
|
||||
- オコゲ
|
||||
---
|
||||
|
||||
{% simplebox 'box_stripe' %}Raspberry Pi 専用デスクトップアプリ『オコゲ』は、Raspberry Pi の GPIO に直接アクセス{% annotate 1 %}することができます。また、特定のモジュールに対応した専用ブロック{% annotate %}も用意されています。
|
||||
ここでは、デスクトップ版『オコゲ』の目玉機能のひとつ、GPIO と TensorFlow.js{% annotate %} を使った「機械学習体験」をご紹介いたします。{% endsimplebox %}
|
||||
|
||||
{% talkicon_l {img: 'tama.webp', name: 'タマちー'} %}{% says_l %}
|
||||
機械学習?目玉焼きの?
|
||||
{% endsays_l %}
|
||||
|
||||
{% talkicon_r {img: 'mimmy.webp', name: 'ミミィ'} %}{% says_r %}
|
||||
目玉機能ね!目玉といえるかどうかは保証できないけど、機械学習はコンピュータ界隈で今もっともホットな話題のひとつではあるわね!
|
||||
{% endsays_r %}
|
||||
|
||||
## 機械学習による画像分類(ぐーちょきぱー)
|
||||
|
||||
一言で機械学習といっても、その内容は多岐に渡ります。『オコゲ』が実装するのは「画像の分類」です。
|
||||
今回は赤外線アレイセンサ(サーマルカメラ)でとらえたじゃんけんのハンドポーズ(ぐー・ちょき・ぱー)を Raspberry Pi で学習してみることにしましょう。
|
||||
|
||||
{% centerimg 'grd-tns_dgst.gif' %}
|
||||
ぐーちょきぱーがわかるタマちー
|
||||
{% endcenterimg %}
|
||||
|
||||
なんかゲームとか作れそうですね。Raspberry Pi とあっちむいてほいとか勝てそうにないですね!
|
||||
|
||||
## 機械学習とは
|
||||
|
||||
> 機械学習とは、データを分析する方法の1つで、データから、「機械」(コンピューター)が自動で「学習し、データの背景にあるルールやパターンを発見する方法。近年では、学習した成果に基づいて「予測・判断」することが重視されるようになった。{% span "tsubuyaki" %}[機械学習 | 用語解説 | 野村総合研究所(NRI)](https://www.nri.com/jp/knowledge/glossary/lst/ka/machine_learning){% endspan %}
|
||||
|
||||
Google で「機械学習」を検索してトップに表示される野村総研の文章を引用してみました。
|
||||
機械学習は「人工知能(AI)」を実現するためのデータ分析技術の1つで、人間や動物が経験を通して自然に学習するのと同様のことをコンピュータでもやろうというものです。
|
||||
本来コンピュータは高速で正確な演算が得意な半面、あまり融通が利きません。例えば、従来のコンピュータに「四角」という図形を教えるとすると
|
||||
|
||||
{% talkicon_r {img: 'square.webp', name: ''} %}{% says_r %}
|
||||
これは四角
|
||||
{% endsays_r %}
|
||||
|
||||
{% talkicon_l {img: 'tama.webp', name: ''} %}{% says_l %}
|
||||
うん
|
||||
{% endsays_l %}
|
||||
|
||||
{% talkicon_r {img: 'parallelogram.webp', name: ''} %}{% says_r %}
|
||||
これも四角
|
||||
{% endsays_r %}
|
||||
|
||||
{% talkicon_l {img: 'tama.webp', name: ''} %}{% says_l %}
|
||||
うん
|
||||
{% endsays_l %}
|
||||
|
||||
{% talkicon_r {img: 'square.webp', name: ''} %}{% says_r %}
|
||||
これは?
|
||||
{% endsays_r %}
|
||||
|
||||
{% talkicon_l {img: 'tama.webp', name: ''} %}{% says_l %}
|
||||
四角
|
||||
{% endsays_l %}
|
||||
|
||||
{% talkicon_r {img: 'rectangle.webp', name: ''} %}{% says_r %}
|
||||
じゃあこれは?
|
||||
{% endsays_r %}
|
||||
|
||||
{% talkicon_l {img: 'tama.webp', name: ''} %}{% think_l %}
|
||||
…?
|
||||
見たことない形だヨ
|
||||
{% endthink_l %}
|
||||
|
||||
すでに覚えたものと全く同じ「正方形」は認識できても、教わっていない「長方形」は理解できません。
|
||||
機械学習の仕組みを組み込んだシステムでは同じように教える(学習させる)と
|
||||
|
||||
{% talkicon_r {img: 'rectangle.webp', name: ''} %}{% says_r %}
|
||||
これは?
|
||||
{% endsays_r %}
|
||||
|
||||
{% talkicon_l {img: 'tama.webp', name: ''} %}{% says_l %}
|
||||
たぶん四角
|
||||
{% endsays_l %}
|
||||
|
||||
この「たぶん」が重要で、この場合、コンピュータは正方形と平行四辺形から「四角」という図形の特徴を
|
||||
{% strong {color:'black'} %}自分で勝手に{% endstrong %}
|
||||
見つけ出し、長方形がその特徴を持っていると判定して「90%の確率で四角」などと答えます。このように学習したデータを使用して新しいデータに対して予測や判断を行うことを「推論」といいます。
|
||||
|
||||
## AMG8833 8×8 赤外線アレイセンサ (Grid-EYE)
|
||||
|
||||
{% floatbox {
|
||||
img: 'grideye-module.webp',
|
||||
title: '',
|
||||
width: '211px',
|
||||
colortheme: 'silver'
|
||||
} %}
|
||||
<i class="fa-solid fa-magnifying-glass"></i> クリックで拡大
|
||||
{% endfloatbox %}
|
||||
|
||||
{% link_new {uri: 'https://www.switch-science.com/catalog/3395/',title: ''} %}Grid-EYE(AMG8833){% endlink_new %} は、Panasonic が開発した縦横8ピクセル(64 画素)の赤外線センサ(サーマルカメラ)です。I2C 接続で利用することができ、記事トップのカバー写真のような簡単な回路で温度データを取得できます。温度データは 64 画素それぞれの温度で、次のようなものです。
|
||||
|
||||
13.25,13.25,18,16.75,16.5,15.75,14.25,12.25,12.75,14,20,20,19.25,17.25,15.25,13.25,14,15.25,21,22,21.25,19.75,15.25,13.25,15,19.25,22,22.5,21.75,19.25,14.25,13,14.75,20.75,23.25,23.25,22.5,17,13.75,13,14.5,18,23,23.5,21.75,15.25,13.25,12.5,14.5,15.75,21.25,23.5,20,15,13.5,13.25,18.25,17.75,21.75,24,20.75,15.25,14.5,14
|
||||
|
||||
これを温度によって色付けして描画することで次のような画像を得ることができます。
|
||||
|
||||
{% centerimg '8x8hand_original.webp' %}{% endcenterimg %}
|
||||
|
||||
{% talkicon_l {img: 'tama.webp', name: ''} %}{% think_l %}
|
||||
見えないし
|
||||
{% endthink_l %}
|
||||
|
||||
{% centerimg '8x8hand_nosmooth.webp' %}{% endcenterimg %}
|
||||
|
||||
20 倍に拡大してみました。これでも何が映っているのかなんとなくわかりますが、スムージングをかけることで少し見やすくなります。
|
||||
|
||||
{% centerimg '8x8hand_smooth.webp' %}{% endcenterimg %}
|
||||
|
||||
手と言われれば、手。ちなみに左下のちょっとだけ赤い部分は、筆者の頭が写り込んだものです。最初、天井に温度が高い部分があるのかと思って上を見たりしていました w
|
||||
|
||||
{% span "tsubuyaki" %}ところで、こんな感じの映像をどこかで見たことがありませんか?
|
||||
そう、{% link_new {uri: 'https://www.nintendo.co.jp/switch/as3ma/traning/index.html?cat=yubi',title: ''} %}これ{% endlink_new %}です。まさか Joy-Con にサーマルカメラが内蔵されていようとは。テレビ CM を見て「おおお…」などと一人で盛り上がっていたのは内緒です。そのほか距離センサなどが同時に使用できるようになっている様子。ジャイロも入っているしホンマどえらいコントローラーやでぇ…{% endspan %}
|
||||
|
||||
## やってみる
|
||||
|
||||
プログラムは以下のようなものです。
|
||||
|
||||
{% centerimg 'grideye-tensor_blocks.webp' %}
|
||||
{% endcenterimg %}
|
||||
|
||||
※ボタンの作成など、本質に関係のない一部のブロックは折り畳み/省略しています。
|
||||
|
||||
センサで取得した画像はキャンバスに表示されるのようになっているので、これを「なし(手がかざされていない状態)」「ぐー」「ちょき」「ぱ」の4つのボタンを使って分類します。
|
||||
キャンバス上の画像に対する推論は常時実行されていて、結果はマスコットキャラクターの横の吹き出しに表示されます。
|
||||
|
||||
学習を行う様子は動画をご覧ください。
|
||||
|
||||
<video loading="lazy" controls="" src="/assets/img/grideye-tensorflow.mp4" style="margin: auto;"></video>
|
||||
|
||||
1. 何も学習していない状態では「undefined」が表示される。センサーに手をかざしてじゃんけんの形を作っても反応なし
|
||||
1. 手をかざさない状態を「なし」ボタンをクリックして学習させる(このとき画面には「じゃんけん…」と表示)
|
||||
1. 同様にして「ぐー」「ちょき」「ぱ」を学習。いずれも2回クリックで正しい推論をするようになった。念のため3回クリック
|
||||
1. その後はハンドポーズを正しく答えることができるようになる
|
||||
|
||||
なんということでしょう。たった 3×4=12 回のクリックでぐーちょきぱー判別の機械学習ができてしまいました。上のプログラムでは使われていませんが、学習したデータを保存・復元するブロックもありますので、育てた学習データを他のプログラムから呼び出して利用することができます。
|
||||
|
||||
## ちょっと詳しく
|
||||
|
||||
『オコゲ』では機械学習ライブラリとして、Google が開発した {% link_new {uri: 'https://www.tensorflow.org/',title: ''} %}TensorFlow{% endlink_new %}(テンサーフロー)を利用しています。Google がこれをオープンソースとして公開したことで、機械学習を誰もがすぐに利用できるようになりました。Google 側もわかりやすいチュートリアルを用意したりして普及に余念がありません。
|
||||
|
||||
しかし、機械学習を一からやろうとすると、大量(画像何千枚とかのレベル)のサンプルデータとそれを学習するための高い処理能力を持つコンピュータ、そして膨大な学習時間が必要です。Raspberry Pi のような非力な子はお呼びではありません。理論も難解で({% link_new {uri: 'https://ja.wikipedia.org/wiki/%E3%83%86%E3%83%B3%E3%82%BD%E3%83%AB',title: ''} %}テンソル{% endlink_new %}って何さ?)、「ばあさんや、なんか流行りらしいからちょっと齧ってみようかね」というわけにはなかなかいきません。先人が作った機械学習のデータ(モデルと呼びます)を利用するという手もありますが、応用がききません。犬種を分類する学習済みモデルを猫に使うことはできないからです。
|
||||
|
||||
そこで考案されたのが「転移学習 (Transfer Learning)」です。ある分野ですでに学習済みのモデルを別の分野での推論に転用して、少ないサンプルデータと学習時間で成果を出そうという手法です。『オコゲ』の機械学習ブロックは、この転移学習を利用して簡単お手軽に機械学習を行うことができるようになっています。
|
||||
|
||||
今回のような「手のポーズの認識」であれば、そのまま使える学習済みモデルとして、Web カメラを使った {% link_new {uri: 'https://github.com/tensorflow/tfjs-models/tree/master/handpose',title: ''} %}MediaPipe Handpose{% endlink_new %}(または{% link_new {uri: 'https://github.com/tensorflow/tfjs-models/tree/master/hand-pose-detection',title: ''} %}こっち{% endlink_new %})も利用できるだろうと思いますが、当たり前だけど手にしか反応しない(前述の「応用がきかない」)のと、Web カメラなので Raspberry Pi である必要がない、なにより「学習をさせる」という面白みがないのでここでは扱うのをやめました。サーマルカメラを使ったここで紹介したやり方なら、ちょっと離れたところから上半身が映るようにして例えば {% link_new {uri: 'https://www.google.com/search?q=%E3%83%A4%E3%83%B3%E3%82%B0%E3%83%9E%E3%83%B3&client=firefox-b-d&channel=crow5&sxsrf=ALiCzsYBHeiV-1OPBaViMgeSnhYAEcV5sg:1652690019938&source=lnms&tbm=isch&sa=X&ved=2ahUKEwjqqb-MzuP3AhUCad4KHUdKDN0Q_AUoAnoECAEQBA&biw=1006&bih=960&dpr=1',title: ''} %}「ヤングマン」のポーズ{% endlink_new %}の認識などもできるかもしれません。
|
||||
|
||||
{% talkicon_r {img: 'mimmy.webp', name: ''} %}{% says_r %}
|
||||
ポーズをとるのが恥ずかしくて試してないんですって!
|
||||
{% endsays_r %}
|
||||
|
||||
{% talkicon_l {img: 'tama.webp', name: ''} %}{% think_l %}
|
||||
誰も見てやしないのにね…
|
||||
{% endthink_l %}
|
||||
|
||||
## 注釈
|
||||
|
||||
{% small 1 %}pigpio デーモンを利用するので厳密には直接ではないが...{% endsmall %}
|
||||
{% small %} 2022 年5月現在、センサー専用ブロックは次の通り。
|
||||
● 赤外線アレイセンサ(サーマルカメラ)AMG8833
|
||||
● ジェスチャーセンサー PAJ7620
|
||||
● 温湿度気圧センサー BME280
|
||||
● 有機 EL ディスプレイ SSD1306{% endsmall %}
|
||||
{% small %}{% link_new {uri: 'https://www.tensorflow.org/js?hl=ja',title: ''} %}TensorFlow.js{% endlink_new %} は、JavaScript 上で利用できる TensorFlow 実装。
|
||||
TensorFlow といえばプログラム言語は Python が定番だが、Raspberry Pi では最新の TensorFlow を使おうと思うと、できないこともないようではあるものの、環境構築やインストールなど、少々敷居が高い。ありていに言うと、面倒くさい。TensorFlow.js は、それ自体が JavaScript で書かれているため、ブラウザ上の JavaScript から簡単に扱うことができ、導入の手間もほとんどかからない。スクリプト言語で書かれているが故の速度的な問題も {% link_new {uri: 'https://github.com/tensorflow/tfjs/tree/master/tfjs-backend-wasm',title: ''} %}WebAssembly バックエンド{% endlink_new %} (wasm) でほぼ解決。そのまま使えるいくつもの有用な{% link_new {uri: 'https://www.tensorflow.org/js/models?hl=ja',title: ''} %}学習済みモデル{% endlink_new %}があらかじめ用意されているうえ、AI に触れたことのない Web フロントエンジニアを意識しているようで、初学者向けの平易なドキュメントも充実している。「画像の転移学習」にはステップバイステップの丁寧な{% link_new {uri: 'https://codelabs.developers.google.com/codelabs/tensorflowjs-teachablemachine-codelab?hl=ja#0',title: ''} %}チュートリアル{% endlink_new %}まで存在する。『オコゲ』を Blockly + Electron で開発してきたのは、もしかしたらこのためだったんじゃないかと思うほどの、あつらえたかのような環境であった。正味の話、前述の転移学習チュートリアルがなかったら機械学習ブロックの導入は諦めてたかもしれない。サンキューグーグル。
|
||||
{% endsmall %}
|
@ -1,84 +0,0 @@
|
||||
---
|
||||
title: "『ゲーム電卓パイ』プロジェクト"
|
||||
description: "Raspberry Pi と LCD キャラクタディスプレイモジュールで「ゲーム電卓」を再現してみる"
|
||||
date: 2022-06-14
|
||||
thumb: "ogp_game-dentak.webp"
|
||||
tags:
|
||||
- オコゲ
|
||||
- デスクトップアプリ版
|
||||
---
|
||||
|
||||
{% simplebox 'box_stripe' %}CASIO のレトロガジェット「ゲーム電卓」を、デスクトップ版『オコゲ』と LCD キャラクタディスプレイモジュールで作成します。
|
||||
※ コード開発と記事は同時進行ですので、まったり更新&内容変更有です。{% endsimplebox %}
|
||||
|
||||
## 「ゲーム電卓」とは
|
||||
|
||||
> ゲーム電卓(ゲームでんたく)は、卓上電子計算機にコンピュータゲームの機能を盛り込んだ多機能化商品。1980年代の電子ゲーム流行の際に大手電卓メーカー等から発売された。 {% link_new {uri: 'https://ja.wikipedia.org/wiki/%E3%82%B2%E3%83%BC%E3%83%A0%E9%9B%BB%E5%8D%93',title: 'Wikipedia'} %}Wikipedia | ゲーム電卓{% endlink_new %}
|
||||
|
||||
ここでは、CASIO から発売された「デジタルインベーダー」SM880 の復刻版である SL-880 を指すこととします。プレイのイメージは、以下のサイトの動画をご覧ください。
|
||||
|
||||
{% link_new {uri: 'https://www.casio.com/jp/basic-calculators/product.SL-880/',title: ''} %}メーカー製品ページ{% endlink_new %}
|
||||
{% link_new {uri: 'https://www.casio.com/content/dam/casio/global/support/manuals/calculators/pdf/004-ja/s/SL-880_WA_JA.pdf',title: ''} %}取扱説明書{% endlink_new %}
|
||||
{% link_new {uri: 'https://kakakumag.com/houseware/?id=11983',title: ''} %}価格.comマガジン{% endlink_new %}
|
||||
|
||||
{% talkicon_r {img: 'mimmy.webp', name: 'ミミィ'} %}{% says_r %}
|
||||
懐かしいヮ・・・
|
||||
「AIM」キーと「FIRE」キーがユルユルになるまで遊んだものョ・・・ハッ
|
||||
{% endsays_r %}
|
||||
{% talkicon_l {img: 'tama.webp', name: 'タマちー'} %}{% think_l %}
|
||||
ミミィって歳いくつなんだろ・・・
|
||||
{% endthink_l %}
|
||||
|
||||
## ハードウェア
|
||||
|
||||
- Raspberry Pi 4B (8GB)
|
||||
- {% link_new {uri: 'https://akizukidenshi.com/catalog/g/gK-12238/',title: ''} %}I2C 接続小型 LCD モジュール{% endlink_new %}
|
||||
- {% link_new {uri: 'https://akizukidenshi.com/catalog/g/gP-04118/',title: ''} %}圧電サウンダ{% endlink_new %}
|
||||
- {% link_new {uri: 'https://www.google.com/search?q=b3f-1000&oq=b3f-1000&aqs=edge..69i57j0i512l2j0i30l2j0i5i30.6451j0j9&sourceid=chrome&ie=UTF-8',title: ''} %}タクトスイッチ{% endlink_new %}
|
||||
|
||||
記事中では、これらの部品をブレッドボードとワイヤ類で接続して使用します。
|
||||
|
||||
## 回路構成
|
||||
|
||||
{% floatbox {
|
||||
img: 'game-dentak_fritzing.webp',
|
||||
title: 'ゲーム電卓パイ',
|
||||
width: '300px',
|
||||
colortheme: 'silver'
|
||||
} %}
|
||||
<i class="fa-solid fa-magnifying-glass"></i> クリックで拡大
|
||||
{% endfloatbox %}
|
||||
|
||||
以下は配線の一例です。
|
||||
|
||||
| LCD キャラクタディスプレイ | Raspberry Pi GPIO (BCM) |
|
||||
|-|-|
|
||||
| A | 3V3(バックライト不要の場合は接続不要) |
|
||||
| VDD | 3V3 |
|
||||
| RESET | 3V3 または 10kΩ でプルアップ |
|
||||
| GND | GND |
|
||||
| SDA | 22<sup>*</sup> |
|
||||
| SCL | 23<sup>*</sup> |
|
||||
| K | GND(バックライト不要の場合は接続不要) |
|
||||
|
||||
\* I2C バス1(GPIO2-3)を使用する場合、キャラクタディスプレイには{% link_new {uri: 'https://akizukidenshi.com/catalog/g/gK-11354/',title: ''} %}こちら{% endlink_new %}を使用
|
||||
|
||||
| 圧電サウンダ | Raspberry Pi GPIO (BCM) |
|
||||
|-|-|
|
||||
| 片方 | 20 |
|
||||
| もう片方 | GND |
|
||||
|
||||
| タクトスイッチ | Raspberry Pi GPIO (BCM) |
|
||||
|-|-|
|
||||
| 左 | 26 |
|
||||
| 右 | 19 |
|
||||
| 左右 GND | GND |
|
||||
|
||||
{% talkicon_l {img: 'tama.webp', name: ''} %}{% says_l %}
|
||||
次回に続く・・・
|
||||
{% endsays_l %}
|
||||
{% talkicon_r {img: 'mimmy.webp', name: ''} %}{% says_r %}
|
||||
キャラクタディスプレイに文字を表示してみるわ!
|
||||
{% endsays_r %}
|
||||
|
||||
{% blogcard {link:'http://localhost/2022-06-21_aqm0802a/'} %}
|
@ -1,136 +0,0 @@
|
||||
---
|
||||
eleventyExcludeFromCollections: true
|
||||
title: "『オコゲ』デスクトップ"
|
||||
description: "Raspberry Pi デスクトップアプリ版『オコゲ』について"
|
||||
date: 2022-06-13
|
||||
thumb: "ogp_ocoge-desktop.webp"
|
||||
tags:
|
||||
- オコゲ
|
||||
- デスクトップアプリ版
|
||||
---
|
||||
|
||||
{% iconbox {
|
||||
iconclass: 'fa-solid fa-triangle-exclamation'
|
||||
} %}
|
||||
このページで紹介するアプリは、限定的な環境において使用することを前提として素人が趣味で開発しているもので、不特定多数の方々の利用を想定したものではありません。ご了承くださいませ。
|
||||
{% endiconbox %}
|
||||
|
||||
{% talkicon_r {img: 'mimmy.webp', name: 'ミミィ'} %}{% says_r %}
|
||||
要するにちゃんと動かなくても文句言うなってことネ!
|
||||
{% endsays_r %}
|
||||
|
||||
{% talkicon_l {img: 'tama.webp', name: 'タマちー'} %}{% think_l %}
|
||||
言い方・・・
|
||||
{% endthink_l %}
|
||||
|
||||
## ダウンロードとインストール
|
||||
|
||||
{% link_new {uri: 'https://ocf.ltt.jp/',title: ''} %}『オコゲ』ダウンロードページ{% endlink_new %}から最新版(最上部に表示されているもの)をダウンロードしてください。
|
||||
ターミナルから次のようにインストールします。
|
||||
|
||||
```bash
|
||||
$ sudo apt install ./ocoge_x.x.x_arm64.deb
|
||||
# x.x.x はバージョン番号
|
||||
```
|
||||
|
||||
## アンインストール
|
||||
|
||||
ターミナルから次のように入力してください。
|
||||
|
||||
```bash
|
||||
$ sudo apt remove ocoge
|
||||
```
|
||||
|
||||
~/.config/ocoge に設定ファイルが残っていて気になる場合は手動で削除してください。
|
||||
|
||||
## 使い方
|
||||
|
||||
GPIO にアクセスするために、PIGPIO デーモンを起動しておきます。
|
||||
|
||||
```bash
|
||||
$ sudo pigpiod
|
||||
```
|
||||
|
||||
上記コマンドは、RPi を再起動するたびに実行する必要があります。
|
||||
サービスとして有効にするには上記コマンドの代わりに、以下を実行してください。
|
||||
|
||||
```bash
|
||||
$ sudo systemctl enable pigpiod
|
||||
$ sudo systemctl start pigpiod
|
||||
```
|
||||
|
||||
『オコゲ』を実行するには、アプリケーションメニュー -> プログラミング -> ocoge で起動するか、ターミナルで ocoge と入力します。
|
||||
基本的な使用方法は体験版やプチレッスンのページをご確認ください。
|
||||
|
||||
{% blogcard {link:'http://localhost/2022-05-07_demo/'} %}
|
||||
|
||||
{% blogcard {link:'http://localhost/2022-05-14_petitlesson-01_typewriter-1/'} %}
|
||||
|
||||
## 制限・制約
|
||||
|
||||
『オコゲ』の動作には開発者都合によるいくつかの制限・制約があります。これらに関して今後の変更・改善は予定されておりません。
|
||||
|
||||
### I2C, シリアル接続は同時に1つまで
|
||||
|
||||
{% inlineimg {img: 'block_serial_open.webp',height: '32px'} %} 及び {% inlineimg {img: 'block_i2c_open.webp',height: '32px'} %} のブロックは同時に1つの機器にしか接続できません。例えば、次のように
|
||||
|
||||
{% centerimg 'block_i2c_twin.webp' %}
|
||||
{% endcenterimg %}
|
||||
|
||||
のようなブロックを実行した場合、アドレス 1 のデバイスに接続する前にアドレス 0 のデバイスとは自動的に切断されます。
|
||||
|
||||
### センサー接続はそれぞれ同時に1つまで
|
||||
|
||||
センサーブロックは種類ごとにそれそれ1つまで接続可能です。例えば
|
||||
|
||||
{% centerimg 'block_i2c_mix.webp' %}
|
||||
{% endcenterimg %}
|
||||
|
||||
は正常に動きますが、
|
||||
|
||||
{% centerimg 'block_bme280_twin.webp' %}
|
||||
{% endcenterimg %}
|
||||
|
||||
は、アドレス 0x76 の機器への接続は自動的に切断され、0x77 への接続だけが残ります。
|
||||
|
||||
## Tips
|
||||
|
||||
### 安全な無限ループ
|
||||
|
||||
{% centerimg 'block_infinit_loop.webp' %}
|
||||
{% endcenterimg %}
|
||||
|
||||
上記のような無限ループを作って実行すると『オコゲ』が操作を受け付けなくなります(ウィンドウ右上の[X]ボタンで『オコゲ』アプリ自体を終了することはできます)。これを避けるには、{% inlineimg {img: 'block_wait_zero.webp',height: '32px'} %} を使って次のようにします。
|
||||
|
||||
{% centerimg 'block_safe_loop.webp' %}
|
||||
{% endcenterimg %}
|
||||
|
||||
このようにすることで、可能な限り早くループを回しつつ、ユーザ入力を受け付けるタイミングを確保することができます。
|
||||
|
||||
### I2C バスの変更
|
||||
|
||||
Raspberry Pi のデフォルトの I2C バス1(GPIO2-3)は内部で 1.8kΩ の抵抗によってプルアップされているため、プルアップ内蔵の I2C モジュールの一部が動作しない恐れがあります({% link_new {uri: 'https://akizukidenshi.com/catalog/faq/goodsfaq.aspx?goods=K-06795',title: ''} %}例{% endlink_new %})。
|
||||
実際やってみると大抵はそのままで問題なく動くのですが、内部プルアップされていないピンに I2C を割り当てることで不具合をあらかじめ回避することもできます。当サイトではバス6(GPIO22-23)を使用しています。
|
||||
|
||||
I2Cバス6を有効化するには、/boot/config.txt に dtoverlay=i2c6,pins_22_23 を追記して再起動します。
|
||||
|
||||
```bash
|
||||
$ echo 'dtoverlay=i2c6,pins_22_23' | sudo tee -a /boot/config.txt
|
||||
$ sudo reboot
|
||||
```
|
||||
|
||||
※ I2C バス1を使用する場合は、「Raspberry Pi の設定」や raspi-config で「I2C を有効」に設定してください。
|
||||
|
||||
●『オコゲ』の設定
|
||||
|
||||
ワークスペース画面でキーボードから「Shift+Ctrl+M」を入力すると、隠されていたメニューバーが表示されます。「Settings」-> 「i2c bus」->「6」を選択し、I2C バスを変更します。
|
||||
|
||||
{% centerimg 'i2c-settings.webp' %}
|
||||
{% endcenterimg %}
|
||||
|
||||
もう一度「Shift+Ctrl+M」を入力するとメニューバーが隠れます。
|
||||
|
||||
|
||||
## ソースコード
|
||||
|
||||
ソースコードは {% link_new {uri: 'https://git.ocoge.club/ocoge.club/ocoge',title: ''} %}ocogIt{% endlink_new %} で公開しています。
|
@ -1,212 +0,0 @@
|
||||
---
|
||||
title: "AQM0802A に文字を表示してみる"
|
||||
description: "『ゲーム電卓パイ』プロジェクト~その2:LCD キャラクタディスプレイモジュール AQM0802A を試してみる"
|
||||
date: 2022-06-21
|
||||
thumb: "ogp_aqm0802a.webp"
|
||||
tags:
|
||||
- オコゲ
|
||||
- デスクトップアプリ版
|
||||
---
|
||||
|
||||
{% talkicon_r {img: 'mimmy.webp', name: 'ミミィ'} %}{% says_r %}
|
||||
とにかく試しに動かしてみるわよ!
|
||||
考えてるヒマがあったら手を動かすのよ手を!
|
||||
{% endsays_r %}
|
||||
|
||||
{% talkicon_l {img: 'tama.webp', name: 'タマちー'} %}{% says_l %}
|
||||
1行目に「ocoge」、2行目に「club」と表示してみよう・・・
|
||||
{% endsays_l %}
|
||||
|
||||
{% simplebox 'box_stripe' %}手順説明の下に『オコゲ』のブロックで組み立てたプログラムコードを畳んで置いてあります。クリックすると開きます。
|
||||
※一部ブロックの形状が現行バージョンと異なります。{% endsimplebox %}
|
||||
|
||||
{% talkicon_r {img: 'mimmy.webp', name: ''} %}{% says_r %}
|
||||
それとこの先の説明では16進数と2進数が何の説明もなく出てくるわ!
|
||||
気になる人は {% link_new {uri: 'https://wa3.i-3-i.info/word1610.html',title: ''} %}16進数{% endlink_new %}, {% link_new {uri: 'https://wa3.i-3-i.info/word1606.html',title: ''} %}2進数{% endlink_new %} あたりを見てネ!
|
||||
{% endsays_r %}
|
||||
|
||||
{% talkicon_l {img: 'tama.webp', name: ''} %}{% think_l %}
|
||||
【{% link_new {uri: 'https://wa3.i-3-i.info/index.html',title: ''} %}「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典{% endlink_new %}】
|
||||
ユルくて好き・・・
|
||||
{% endthink_l %}
|
||||
|
||||
## I2C アドレス
|
||||
|
||||
次のコマンドで確認します。
|
||||
|
||||
```bash
|
||||
$ i2cdetect -y 6
|
||||
0 1 2 3 4 5 6 7 8 9 a b c d e f
|
||||
00: -- -- -- -- -- -- -- --
|
||||
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
|
||||
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
|
||||
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- 3e --
|
||||
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
|
||||
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
|
||||
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
|
||||
70: -- -- -- -- -- -- -- --
|
||||
```
|
||||
|
||||
AQM0802A の I2C アドレスは 0x3e と分かりました。
|
||||
|
||||
{% blockprogram {
|
||||
summary: 'I2C デバイスを開く'
|
||||
} %}
|
||||
{% centerimg 'game-dentak_i2copen.webp' %}
|
||||
GPIO を有効にして I2C デバイスに接続する
|
||||
{% endcenterimg %}
|
||||
|
||||
デバイスに限らず、何かに接続して使用可能な状態にすることをコンピュータ用語では「開く (open)」と表現します。
|
||||
{% endblockprogram %}
|
||||
|
||||
## コマンド書き込み
|
||||
|
||||
レジスタ 0x00 にコマンドコード(1バイト)を書き込むことで、AQM0802A にコマンド(命令)を送信することができます。
|
||||
|
||||
{% blockprogram {
|
||||
summary: 'コマンドを書き込む'
|
||||
} %}
|
||||
繰り返し使用するプログラムなので、関数として分離しておきます。
|
||||
|
||||
{% centerimg 'game-dentak_func-command.webp' %}
|
||||
コマンド書き込み関数
|
||||
{% endcenterimg %}
|
||||
|
||||
コマンド書き込み毎に待ち時間が必要です。待ち時間の長さはコマンドの種類によって若干の違いがあるようですが、ここでは無視して100分の1秒で統一します。
|
||||
|
||||
{% talkicon_r {img: 'mimmy.webp', name: ''} %}{% says_r %}
|
||||
ちゃんと動いているから大丈夫!
|
||||
たぶん!
|
||||
{% endsays_r %}
|
||||
{% endblockprogram %}
|
||||
|
||||
## データ書き込み
|
||||
|
||||
レジスタ 0x40 にバイトコードを書き込むことで、AQM0802A 内蔵の RAM (DDRAM/CGRAM) にデータを書き込むことができます。DDRAM にデータを書き込むと画面に文字を表示することができます。CGRAM は自作文字のデータを登録しておくことができます。
|
||||
|
||||
{% blockprogram {
|
||||
summary: 'データを書き込む'
|
||||
} %}
|
||||
こちらも繰り返し使用するので関数にしておきます。
|
||||
|
||||
{% centerimg 'game-dentak_func-data.webp' %}
|
||||
データ書き込み関数
|
||||
{% endcenterimg %}
|
||||
|
||||
{% endblockprogram %}
|
||||
|
||||
## 初期化
|
||||
|
||||
{% link_new {uri: 'https://akizukidenshi.com/download/ds/xiamen/AQM0802.pdf',title: ''} %}秋月電子のデータシート{% endlink_new %}に従って初期化します。
|
||||
|
||||
以下のコマンドコードを、順にレジスタ 0x00 に書き込みます。
|
||||
|
||||
1. 0x38 (機能設定:表示行数とか文字の大きさとか)
|
||||
1. 0x39 (拡張コマンドモード開始)
|
||||
1. 0x14 (内蔵発振器周波数)
|
||||
1. 0x70 (コントラスト)
|
||||
1. 0x56 (3V動作)
|
||||
1. 0x6c (フォロワ回路のオンオフと増幅率)
|
||||
1. 0x38 (拡張コマンドモード終了)
|
||||
1. 0x0c (ディスプレイオン・カーソルオフ)
|
||||
1. 0x01 (ディスプレイクリア)
|
||||
|
||||
{% blockprogram {
|
||||
summary: 'AQM0802A を初期化'
|
||||
} %}
|
||||
{% centerimg 'game-dentak_init.webp' %}
|
||||
初期化手順
|
||||
{% endcenterimg %}
|
||||
{% endblockprogram %}
|
||||
|
||||
## 文字表示
|
||||
|
||||
DDRAM にデータを書き込むと画面に文字を表示します。
|
||||
|
||||
### DDRAM アドレスを指定
|
||||
|
||||
文字を表示するディスプレイ上の位置(2行目の3文字目とか)を指定します。
|
||||
|
||||
(データシートコマンドコード表より)
|
||||
| Instruction |DB7|DB6|DB5|DB4|DB3|DB2|DB1|DB0|
|
||||
|-------------------|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
|
||||
| Set DDRAM address | 1 |AC6|AC5|AC4|AC3|AC2|AC1|AC0|
|
||||
|
||||
データシートではコマンドは8桁(8ビット)の2進数で書かれています。それぞれのビットに役割があり、最上位ビットを立てると DDRAM アドレス指定コマンドとして働きます。残りの7ビットでアドレス値を指定します。DDRAM アドレス値と表示位置の対応は次の通りです。(横の〇文字目を便宜上「列」と表現することにします)
|
||||
|
||||
| |1列目|2列目|3列目|4列目|5列目|6列目|7列目|8列目|
|
||||
|:---:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|
|
||||
|1行目|0x00|0x01|0x02|0x03|0x04|0x05|0x06|0x07|
|
||||
|2行目|0x40|0x41|0x42|0x43|0x44|0x45|0x46|0x47|
|
||||
|
||||
以上から、ディスプレイ上の文字表示位置を指定するには以下のようにします。
|
||||
|
||||
* レジスタ 0x00 に、「0x80(最上位ビット) +(上の表のアドレス値)」を書き込む
|
||||
|
||||
### 文字表示
|
||||
|
||||
DDRAM アドレス指定コマンドを書き込んだのちにデータ書き込みを行うことで、指定した位置に文字を表示することができます。文字コードは以下の通りです。
|
||||
|
||||
{% floatbox {
|
||||
img: 'game-dentak_character-patterns.webp',
|
||||
title: 'キャラクターパターン表',
|
||||
width: '400px',
|
||||
colortheme: 'silver'
|
||||
} %}
|
||||
<i class="fa-solid fa-magnifying-glass"></i> クリックで拡大
|
||||
{% endfloatbox %}
|
||||
|
||||
コード `0010 0000` からコード `0111 1101` まで、すなわちキーボードで入力できる半角(1バイト)の英数字・記号の大部分は UTF-8 (ascii) のコードと一致していますので、表示したい文字列がこの範囲内であれば、そのままデータとして(レジスタ 0x40 へ)書き込めば良いでしょう。
|
||||
|
||||
{% blockprogram {
|
||||
summary: 'AQM0802A に文字を表示する'
|
||||
} %}
|
||||
行と列を指定して文字列表示を行う関数を作成しましょう。
|
||||
|
||||
{% floatbox {
|
||||
img: 'game-dentak_func-dispchars.webp',
|
||||
title: '文字列表示関数',
|
||||
width: '800px',
|
||||
colortheme: 'silver'
|
||||
} %}
|
||||
<i class="fa-solid fa-magnifying-glass"></i> 文字列表示関数
|
||||
{% endfloatbox %}
|
||||
|
||||
「行」と「列」は、内部的には0(ゼロ)から始まる数え方をするので、それぞれ1を引きます。
|
||||
|
||||
大文字・小文字・記号などがちゃんと表示されるかどうか、表示位置指定の確認も含めて、1行目に「{ocoge}」、2行目は右に3文字ずらして4文字目から「CLUB!」と5秒間表示することにしましょう。
|
||||
|
||||
{% centerimg 'game-dentak_call-dispchars.webp' %}
|
||||
{% endcenterimg %}
|
||||
|
||||
最後に、ディスプレイをオフにしてデバイスとの接続を切るなど後片付けをします。ディスプレイの表示をオフにするにはコマンド `0x08` を書き込みます。
|
||||
|
||||
{% centerimg 'game-dentak_okatazuke.webp' %}
|
||||
{% endcenterimg %}
|
||||
|
||||
{% endblockprogram %}
|
||||
|
||||
## まとめ
|
||||
|
||||
今回作成したプログラムはこのようになりました。
|
||||
|
||||
{% floatbox {
|
||||
img: 'game-dentak_display-characters.webp',
|
||||
title: 'AQM0802A に文字を表示してみるプログラム',
|
||||
width: '400px',
|
||||
colortheme: 'silver'
|
||||
} %}
|
||||
<i class="fa-solid fa-magnifying-glass"></i> AQM0802A に文字を表示してみるプログラム
|
||||
{% endfloatbox %}
|
||||
|
||||
{% floatbox {
|
||||
img: 'game-dentak_display-characters-result.webp',
|
||||
title: 'AQM0802A に文字を表示してみた結果',
|
||||
width: '200px',
|
||||
colortheme: 'silver'
|
||||
} %}
|
||||
<i class="fa-solid fa-magnifying-glass"></i> 実行結果
|
||||
{% endfloatbox %}
|
||||
|
||||
バックライトは点灯させていません。期待通りの動作をしているようです。メデタシ。
|
||||
次回はゲームのルール確認と自作文字(UFO と残機表示)の作成をしましょう。
|
@ -1,57 +0,0 @@
|
||||
---
|
||||
eleventyExcludeFromCollections: true
|
||||
title: "海外からいろいろ届いたよ"
|
||||
description: "RP2040 ベースのマイクロコントローラユニットをいくつか買ってみました。"
|
||||
date: 2022-07-26
|
||||
thumb: "ogp_rp2040boards.webp"
|
||||
tags:
|
||||
- RP2040
|
||||
- Pico
|
||||
---
|
||||
|
||||
{% talkicon_r {img: 'mimmy.webp', name: 'ミミィ'} %}{% says_r %}
|
||||
ちょっとRP2040マイクロコントローラユニット (MCU) を色々買ってみたワ!
|
||||
RP2040 はラズベリーパイ財団が開発したマイクロコントローラで、Raspberry Pi Pico に搭載されているものヨ!
|
||||
{% endsays_r %}
|
||||
|
||||
> マイクロコントローラとは、CPU(Central Processing Unit:中央処理装置)とメモリ等の周辺機能が1チップに集積された半導体の一つです。日本では、「マイコン」と呼ばれることもありますが、海外では通じない事が多いです。CPUは、ソフトウェアによるプログラムに従い演算や制御を行います。そのためにプログラムを格納するROMや演算データを一時記憶するRAMなどのメモリが周辺機能として内蔵されることが多く、その他にタイマやADコンバータと呼ばれる様々な機能も追加され現在では家電やOA機器、車載など多くのアプリケーションに採用されています。{% link_new {uri: 'https://www.epson.jp/prod/semicon/products/micro_controller/about_mcu1.htm',title: 'EPSON'} %}マイクロコントローラ(MCU)とは? - エプソン{% endlink_new %}
|
||||
|
||||
{% centerimg 'rp2040boards.webp' %}
|
||||
|
||||
{% endcenterimg %}
|
||||
|
||||
| No | MCU | フラッシュ | ピン数 | 外寸<sup>1</sup> | USB | 価格 |
|
||||
|-|-|-|-|-|-|-|
|
||||
| 1 | {% link_new {uri: 'https://www.raspberrypi.com/documentation/microcontrollers/raspberry-pi-pico.html',title: 'Raspberry Pi Foundation'} %}Raspberry Pi Pico{% endlink_new %} | 2MB | 20x2+3 | 51×21mm | micro | 527円<sup>2</sup> |
|
||||
| 2 | Raspberry Pi Pico W | 2MB | 20x2+3 | 51×21mm | micro | 810円<sup>2</sup> |
|
||||
| 3 | {% link_new {uri: 'https://www.waveshare.com/rp2040-zero.htm',title: 'WAVESHSRE'} %}Waveshare RP2040-Zero{% endlink_new %} | 2MB | 9x2+5 | 23.5x18mm | Type-C | 517円<sup>3</sup> |
|
||||
| 4 | {% link_new {uri: 'https://shop.pimoroni.com/products/tiny-2040?variant=39560012234835',title: '海賊ロボ忍者さる'} %}PIMORONI Tiny 2040{% endlink_new %} | 8MB<sup>4</sup> | 8x2+3 | 21x18mm | Type-C | 1,134円<sup>2</sup> |
|
||||
| 5 | RP2040 Black Board New Type | 4MB<sup>5</sup> | 20x2+4 | 53x23mm | Type-C | 405円<sup>3</sup> |
|
||||
{% small 1 %}基板部のみの大きさ{% endsmall %}
|
||||
{% small %}PIMORONIの日本円表示価格 (送料別 - 小物なので800円くらい){% endsmall %}
|
||||
{% small %}AliExpressの価格 (送料込・当方購入時の最低価格){% endsmall %}
|
||||
{% small %}2MB版もある{% endsmall %}
|
||||
{% small %}16MB版もある (700円くらい) {% endsmall %}
|
||||
|
||||
{% talkicon_l {img: 'tama.webp', name: 'タマちー'} %}{% says_l %}
|
||||
1,2 はオリジナル、4 はイギリスの有名ベンダーの製品、3,5 は中華製なの・・・
|
||||
{% endsays_l %}
|
||||
|
||||
{% talkicon_r {img: 'mimmy.webp', name: ''} %}{% says_r %}
|
||||
中華製品は基盤の仕上げとか動作のクセとか、些細なところで大きなストレスを与えてくるのよネ!
|
||||
本質と違うところで苦労したくなければ中華は避けるべきヨ!
|
||||
{% endsays_r %}
|
||||
|
||||
{% talkicon_l {img: 'tama.webp', name: ''} %}{% think_l %}
|
||||
ヘンなモジュールで安く遊びたい変態さんには中華製超オススメ・・・
|
||||
{% endthink_l %}
|
||||
|
||||
{% talkicon_r {img: 'mimmy.webp', name: ''} %}{% says_r %}
|
||||
アンタ・・・
|
||||
{% endsays_r %}
|
||||
|
||||
{% simplebox 'box_stripe' %}
|
||||
ただ、オリジナルの Pico に全く問題がないのかというとそうでもなく、例えばリセットボタンがない、基盤の上面にピン番号が印刷されていない、USB 端子形状が micro USB であるなど、使い勝手の悪い部分があります。まあこの辺りは Raspberry Pi コンピュータも同様なので、そういう思想なのでしょう。
|
||||
そういうわけで、現在当方では小さいサイズに Tiny 2040 、普通サイズに RP2040 Black Board New Type を使用中。どちらも、リセットボタン、ピン番号のシルク印刷、USB Type-C、大容量のフラッシュメモリ、フルカラーLED搭載。加えて後者は送込み400円のお陰で、やらかしてしまっても怖くないのがイイ。
|
||||
{% endsimplebox %}
|
||||
|
@ -1,70 +0,0 @@
|
||||
---
|
||||
title: "ジェスチャーセンサ PAJ7620 を試す"
|
||||
description: "ちょっと楽しそうなハンドジェスチャセンサモジュール PAJ7620 を入手したので、『オコゲ』を使って遊んでみました。"
|
||||
date: 2022-08-03
|
||||
thumb: "ogp_paj7620.webp"
|
||||
tags:
|
||||
- オコゲ
|
||||
- デスクトップアプリ版
|
||||
- センサー
|
||||
- 動画
|
||||
---
|
||||
|
||||
{% simplebox 'box_stripe' %}ちょっと楽しそうなハンドジェスチャセンサモジュール PAJ7620 を入手したので、『オコゲ』を使って遊んでみました。{% endsimplebox %}
|
||||
|
||||
## PAJ7620 概要
|
||||
|
||||
- 9種類のジェスチャーを認識(上下左右、前後、時計回り・反時計回り、波)
|
||||
- I2C インターフェース
|
||||
- 動作電圧:3.3V
|
||||
- 検出範囲:5~10cm
|
||||
- {% link_new {uri: 'https://wiki.seeedstudio.com/Grove-Gesture_v1.0/#resources',title: ''} %}データシート{% endlink_new %}
|
||||
|
||||
## 配線
|
||||
|
||||
{% floatbox {
|
||||
img: 'paj7620.webp',
|
||||
title: 'ジェスチャーセンサー配線',
|
||||
width: '200px',
|
||||
colortheme: 'silver'
|
||||
} %}
|
||||
<i class="fa-solid fa-magnifying-glass"></i> クリックで拡大
|
||||
{% endfloatbox %}
|
||||
|
||||
VIN→3.3V
|
||||
GND→GND
|
||||
SDA→GPIO2
|
||||
SCL→GPIO3
|
||||
INT→オープン
|
||||
|
||||
## Raspberry Pi の設定
|
||||
|
||||
<span style="font-size: 20px; color: #ff0000"><i class="fab fa-raspberry-pi"></i></span>メニュー→設定→Raspberry Piの設定→「インターフェース」タブで I2C を有効にしておく
|
||||
|
||||
## 『オコゲ』プログラム
|
||||
|
||||
{% floatbox {
|
||||
img: 'gesture_blockprg.webp',
|
||||
title: '『オコゲ』ブロックプログラム',
|
||||
width: '400px',
|
||||
colortheme: 'silver'
|
||||
} %}
|
||||
<i class="fa-solid fa-magnifying-glass"></i> クリックで拡大
|
||||
{% endfloatbox %}
|
||||
|
||||
### 動画サンプル
|
||||
|
||||
PAJ7620を『オコゲ』で使用している動画です。
|
||||
※ 古い動画なので現在の『オコゲ』とは見た目が色々と異なっています。ご了承ください。
|
||||
|
||||
{% youtube {
|
||||
videoid: '-JwvqsHIYU0'
|
||||
} %}
|
||||
ジェスチャーセンサ PAJ7620 を試す
|
||||
{% endyoutube %}
|
||||
|
||||
いろいろ遊べそうな雰囲気。Raspberry Pi Zero をネットラジオ再生専用機にしたりしているので、ジェスチャ対応にしてみたら面白いかもしれない。
|
||||
|
||||
<span class="tsubuyaki">昔見たイギリス製SFテレビドラマ「SPACE: 1999」を思い出した。「The Last Enemy(邦題:宇宙戦艦ミサイル発射!)」というエピソードで、その宇宙戦艦「 サタゼウス 」のコントロールパネルがまさにこんな感じの非接触式ジェスチャ操作で、当時その未来的な雰囲気がチョーカッコイイと感じたのを覚えている。今見るとこれはこれで「レトロ未来感」が逆にカッコイイのだが。
|
||||
関係ないけどサタゼウスの乗組員が全員女性(全員女性の惑星と全員男性の惑星が戦争しているという設定w)で、これまた{% link_new {uri: 'https://www.imdb.com/title/tt0706347/mediaviewer/rm1324681984',title: 'IMBD'} %}カッコイイ{% endlink_new %}のだった。リンク先の写真の直後、戦艦が爆破されてみんな死んじゃうけど。敵だったからネ</span>
|
||||
|
@ -1,7 +1,8 @@
|
||||
---
|
||||
title: "おこげ倶楽部自宅サーバについて"
|
||||
description: "ocoge.club は Raspberry Pi 自宅サーバにてホスティングされています。Webサーバ、メールサーバ稼働中。さらに..."
|
||||
date: 2023-03-22
|
||||
date: 2199-12-27
|
||||
date_disp: 2023-03-22
|
||||
thumb: "ocoge-server_2023.webp"
|
||||
tags:
|
||||
- 技術情報
|
||||
|
@ -1,46 +0,0 @@
|
||||
---
|
||||
title: "『オコゲ』で有機ELミニディスプレイ"
|
||||
description: "SSD1306 搭載 OLED モジュールに線や文字を描画する『オコゲ』ブロック"
|
||||
date: Last Modified
|
||||
thumb: "ssd1306_ja.webp"
|
||||
tags:
|
||||
- オコゲ
|
||||
- デスクトップアプリ版
|
||||
---
|
||||
|
||||
{% simplebox 'box_stripe' %}制御チップに SSD1306 を搭載した有機 EL ディスプレイ (OLED) モジュールに線や文字を描画するブロックを『オコゲ』に実装しました。{% endsimplebox %}
|
||||
|
||||
{% floatbox {
|
||||
img: 'ssd1306_square.webp',
|
||||
title: 'クリックで拡大',
|
||||
width: '180px',
|
||||
colortheme: 'silver'
|
||||
} %}
|
||||
線も描けます。
|
||||
{% endfloatbox %}
|
||||
|
||||
{% blockprogram {
|
||||
summary: '日本語表示例'
|
||||
} %}
|
||||
{% centerimg 'oled_iamacat_block.webp' %}
|
||||
アイキャッチ画像の日本語表示プログラム
|
||||
{% endcenterimg %}
|
||||
{% endblockprogram %}
|
||||
|
||||
{% talkicon_r {img: 'mimmy.webp', name: 'ミミィ'} %}{% says_r %}
|
||||
対応モジュールは I2C 接続のものヨ!
|
||||
残念だけど SPI 接続(7ピン)は対応していないワ!
|
||||
あと画面サイズも 128x64, 128x32, 96x16 の3種類だけヨ!
|
||||
{% endsays_r %}
|
||||
{% talkicon_l {img: 'tama.webp', name: 'タマちー'} %}{% says_l %}
|
||||
~~そのうち PNG 表示とかもできるようにしたい・・・~~
|
||||
対応した・・・
|
||||
{% endsays_l %}
|
||||
{% floatbox {
|
||||
img: 'oled_png.webp',
|
||||
title: 'クリックで拡大',
|
||||
width: '180px',
|
||||
colortheme: 'silver'
|
||||
} %}
|
||||
<i class="fa-solid fa-magnifying-glass"></i> PNG 表示
|
||||
{% endfloatbox %}
|
@ -1,47 +0,0 @@
|
||||
---
|
||||
eleventyExcludeFromCollections: false
|
||||
title: "UART 静電容量式指紋センサブロック"
|
||||
description: "シリアル接続の指紋センサ SFM-V1.7 用ブロックを「モジュール」に追加しました。"
|
||||
date: Last Modified
|
||||
thumb: "fingerprint_on_tofu.webp"
|
||||
tags:
|
||||
- オコゲ
|
||||
- デスクトップアプリ版
|
||||
---
|
||||
|
||||
{% simplebox 'box_stripe' %}シリアル接続の指紋センサ {% link_new {uri: 'https://x2robotics.ca/uart-capacitive-fingerprint-sensor-kit-sfm-v-1-7',title: 'X2 ROBOTICS'} %}SFM-V1.7{% endlink_new %} 用ブロックを『オコゲ』のカテゴリ「モジュール」に追加しました。指紋の登録・照合・全削除に対応。
|
||||
{% endsimplebox %}
|
||||
{% floatbox {
|
||||
img: 'fingerprint_blocks.webp',
|
||||
title: '指紋センサブロック',
|
||||
width: '200px',
|
||||
colortheme: 'silver'
|
||||
} %}
|
||||
<i class="fa-solid fa-magnifying-glass"></i> クリックで拡大
|
||||
{% endfloatbox %}
|
||||
|
||||
{% talkicon_r {img: 'mimmy.webp', name: 'ミミィ'} %}{% says_r %}
|
||||
このモジュールは、指紋読み取り・記録・データベース照合まで全て内部でやってくれる「オールインワン」なモジュールなのヨ!
|
||||
{% endsays_r %}
|
||||
{% talkicon_l {img: 'tama.webp', name: 'タマちー'} %}{% says_l %}
|
||||
モジュールとのやりとりも UART 接続でとっても簡単…
|
||||
{% endsays_l %}
|
||||
{% talkicon_r {img: 'mimmy.webp', name: ''} %}{% says_r %}
|
||||
でもネット上に資料がほとんど無くてちょっと困ったワ!
|
||||
光学式の資料ならそれなりにあるんだけど、仕様がちょっと違うみたいなのよネ!
|
||||
{% endsays_r %}
|
||||
{% talkicon_l {img: 'tama.webp', name: ''} %}{% says_l %}
|
||||
ESP32 用の C++ ライブラリがあったから必要な部分だけ Node.js 用に書き換えた...
|
||||
ガンバった...
|
||||
{% endsays_l %}
|
||||
|
||||
### 参考:指紋登録プログラム
|
||||
|
||||
{% floatbox {
|
||||
img: 'fingerprint_registration.webp',
|
||||
title: '指紋登録 (3C3R)',
|
||||
width: '200px',
|
||||
colortheme: 'silver'
|
||||
} %}
|
||||
<i class="fa-solid fa-magnifying-glass"></i> クリックで拡大
|
||||
{% endfloatbox %}
|
@ -1,7 +1,7 @@
|
||||
---
|
||||
title: "『おこげ俱楽部』"
|
||||
description: "『情報コース』ご紹介、独自のブロックプログラミング環境『オコゲ』、教室での実習内容や周辺技術情報等のアウトプット"
|
||||
date: 2199-01-01
|
||||
date: 2199-12-31
|
||||
date_disp: 2022-04-25
|
||||
thumb: "ogp_ocogeclub-duck.webp"
|
||||
tags: ICT-Course
|
||||
|
Before Width: | Height: | Size: 200 B |
Before Width: | Height: | Size: 136 B |
Before Width: | Height: | Size: 8.7 KiB |
Before Width: | Height: | Size: 4.4 KiB |
Before Width: | Height: | Size: 7.2 KiB |
Before Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 2.3 KiB |
Before Width: | Height: | Size: 3.0 KiB |
Before Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 904 B |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 44 KiB |
Before Width: | Height: | Size: 50 KiB |
Before Width: | Height: | Size: 8.7 KiB |
Before Width: | Height: | Size: 142 KiB |
Before Width: | Height: | Size: 55 KiB |
Before Width: | Height: | Size: 65 KiB |
Before Width: | Height: | Size: 37 KiB |
Before Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 6.5 KiB |
Before Width: | Height: | Size: 7.7 KiB |
Before Width: | Height: | Size: 9.1 KiB |
Before Width: | Height: | Size: 54 KiB |
Before Width: | Height: | Size: 304 KiB |
Before Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 47 KiB |
Before Width: | Height: | Size: 5.2 KiB |
@ -1,5 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<body style="background-color: green;">
|
||||
<img src="./tama_ko.webp">
|
||||
</body>
|
Before Width: | Height: | Size: 107 KiB |
Before Width: | Height: | Size: 42 KiB |
Before Width: | Height: | Size: 117 KiB |
Before Width: | Height: | Size: 143 KiB |
Before Width: | Height: | Size: 52 KiB |
Before Width: | Height: | Size: 58 KiB |
Before Width: | Height: | Size: 45 KiB |
Before Width: | Height: | Size: 118 KiB |
Before Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 26 KiB |
Before Width: | Height: | Size: 46 KiB |
Before Width: | Height: | Size: 79 KiB |
Before Width: | Height: | Size: 41 KiB |
Before Width: | Height: | Size: 448 B |
Before Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 134 B |
Before Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 126 B |
Before Width: | Height: | Size: 70 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 50 KiB |
Before Width: | Height: | Size: 22 KiB |
Before Width: | Height: | Size: 9.6 KiB |
@ -1,7 +1,8 @@
|
||||
---
|
||||
title: "プチレッスン目次"
|
||||
description: "各レッスンに進む前にお読みください。問題形式で『オコゲ』でのプログラミング学習を体験することができます。"
|
||||
date: 2022-05-14
|
||||
date: 2199-12-24
|
||||
date_disp:: 2022-05-14
|
||||
thumb: "raspberrycake_ogp.webp"
|
||||
tags:
|
||||
- オコゲ
|
||||
|
@ -1,12 +0,0 @@
|
||||
---
|
||||
layout: default
|
||||
title: Search
|
||||
---
|
||||
|
||||
<div class="flex border-gray-100 border-t items-center mb-8 md:mx-auto md:w-2/3 px-4 py-3 rounded shadow text-gray-600 w-full">
|
||||
{% include "icons/search.svg" %}
|
||||
<label class="visually-hidden" for="searchbox">Search here ...</label>
|
||||
<input type="search" id="searchbox" class="focus:outline-none ml-3 w-full" placeholder="Search here ...">
|
||||
</div>
|
||||
<div id="info" class="text-center mb-5 mx-auto text-lg text-gray-600"></div>
|
||||
<div id="wrapper" class="flex flex-wrap -mx-2"></div>
|
@ -11,7 +11,7 @@ eleventyComputed:
|
||||
---
|
||||
|
||||
<div class="mb-8 text-center">
|
||||
<h3 class="text-xl font-medium text-gray-700 my-0">Tagged with</h3>
|
||||
<h3 class="text-xl font-medium text-slate-700 my-0">Tagged with</h3>
|
||||
<h1 class="text-4xl font-medium leading-normal mt-0">{{ paged.tagName | capitalize }}</h1>
|
||||
</div>
|
||||
<div class="flex flex-wrap -mx-2">
|
||||
|