情報の荒波から卒業──ラズパイでつくる、ミニマリスト向け一行ニュース表示デバイス

FabScene(ファブシーン)

目まぐるしく変化する国際情勢。陰謀論や誹謗中傷がうずまくSNS。情報社会の荒波にもまれていると、気づけばぐったりしてしまいませんか? だからといって、何も知らないままでいるのも、どこか不安が残る。

余計なストレスを抱えずに、最新情報だけを受け取りたい。そんな、わがままな欲望を叶えるために、こんな装置を開発した。

FabScene(ファブシーン)

名づけて「一行新聞」。

両手に収まる小さな端末に、ディスプレイがひとつ。電源を入れるとネットに接続し、最新ニュースの「見出しだけ」を取得。カタカナのみで、シンプルに表示する。

わずらわしいコメントも、気を散らす画像もない。時代の流れを最低限だけ追いながら、自分の生活リズムは乱さない。そんな、ミニマル志向の情報端末だ。

FabScene(ファブシーン)
Yahoo!ニュースの見出しを拾い、スクロール表示

起動から1分後、あるいはリセットボタンを押すと、新しいニュースを取得し、見出しが更新される。新幹線の電光掲示板を眺めるような感覚で、最新ニュースをそれとなく味わえる。一方で、だらだらとショート動画を見続けたり、インターネットの海を漂ったりすることはない。

わがままな現代人のための、控えめな情報装置なのだ。

目次

コンセントから離れるための、乾電池で動かす拡張ボード

本装置の心臓部には、Raspberry Pi Pico Wを採用している。Wi-Fiに接続し、ニュースサイトのRSSから見出しを取得。テキストをカタカナに変換し、LCDへ表示するという3ステップで動作する。

FabScene(ファブシーン)
デバイスの構成と動作の流れ

設計で最もこだわったのは、「他の情報端末から距離を取る」ことだ。

表示情報がいくら少なくても、すぐ隣にパソコンやスマートフォンがあっては意味がない。情報摂取の導線を極限まで短くするため、扱いやすく小型なRaspberry Pi Pico Wを選択した。

さらに重要なのが電源である。コンセントに縛られたデバイスでは、結局ほかの電子機器と同じ空間に置かれてしまう。独立した装置として成立させるには、独立した電源が欠かせない。

FabScene(ファブシーン)
FabScene(ファブシーン)
単3電池拡張ボードの構成

そこで採用したのが、ビット・トレード・ワンから発売されている「AD2040AA Raspberry Pi Pico 単3電池拡張ボード」だ。Raspberry Pi Pico用の電池拡張基板として、単3形電池2本用のホルダーを備え、ピンヘッダ実装済みのRaspberry Pi Picoを差し込むだけで乾電池駆動を可能にする。

GPIO用のIOピンソケットは2列分確保されており、基板に載せても拡張性は損なわれない。さらにGroveコネクターも搭載されているため、各種センサーやアクチュエーターとの接続も容易だ。

小型で扱いやすいRaspberry Pi Picoだが、電源端子はMicro USB(Micro-B)であり、外部バッテリー駆動にはひと手間かかる。この拡張ボードは、そうした細かな不便さを自然に解消してくれる設計になっている。電源スイッチやリセットスイッチも実装されており、開発や検証の際の操作性も高い。

FabScene(ファブシーン)
拡張ボードを活用したデバイス本体内部

「一行新聞」の内部は非常にシンプルだ。

Raspberry Pi Pico WHを単3電池拡張ボードに差し込み、単3電池2本で給電。Groveコネクタ経由で接続したLCDに文字を表示するだけの構成である。コンセントに縛られず、電源をONにすれば単体で動作する。Raspberry Pi Picoのミニマルな特性を活かした装置と、相性の良い拡張ボードといえるだろう。

RSSからニュースの見出しを取得

ここからは、装置の中身となるプログラムを紹介する。

Raspberry Pi Pico WHはThonnyなどのMicroPython環境でも開発できるが、取り回しのしやすさを考え、Arduino IDEを使用した。ボードマネージャから「Raspberry Pi Pico/RP2040…」をインストールし、書き込み先ボードとして「Raspberry Pi Pico W」を選択しておこう。

FabScene(ファブシーン)
書き込み先ボードにRaspberry Pi Pico Wを選択する

ニュースの取得には、Wi-Fi機能を標準搭載したRaspberry Pi Pico Wの特性を活かす。オンラインで公開されているニュースサイトのRSSフィードを直接読み込み、見出し部分だけを抽出する構成とした。プログラムはこちらのサイトの制作例を参考にしている。

#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <HTTPClient.h>


const char* ssid     = "SSID";
const char* password = "PASS";
const char* RSS_URL  = "https://news.yahoo.co.jp/rss/topics/top-picks.xml";


static const int N = 5; // 上位N件からランダムに1件


String fetchRandomItemTitle(const char* url, int limitN) {
 WiFiClientSecure client;
 client.setInsecure();


 HTTPClient https;
 if (!https.begin(client, url)) return "BEGIN_FAIL";


 int code = https.GET();
 if (code != 200) { https.end(); return "HTTP_" + String(code); }


 WiFiClient* s = https.getStreamPtr();
 String buf;


 int seen = 0;
 String picked = "NO_ITEM";


 while (https.connected() && seen < limitN) {
   while (s->available() && seen < limitN) {
     buf += (char)s->read();


     int itemPos = buf.indexOf("<item>");
     if (itemPos >= 0) {
       int t1 = buf.indexOf("<title>", itemPos);
       int t2 = (t1 >= 0) ? buf.indexOf("</title>", t1 + 7) : -1;


       if (t1 >= 0 && t2 >= 0) {
         String title = buf.substring(t1 + 7, t2);
         title.trim();
         seen++;
         if (random(seen) == 0) picked = title; // 1/seen で入れ替え
         buf = buf.substring(t2 + 8); // 進める
       }
     }
     if (buf.length() > 4096) buf.remove(0, buf.length() - 1024);
   }
   delay(1);
 }
 https.end();
 return picked;
}


void setup() {
 Serial.begin(115200);


 WiFi.begin(ssid, password);
 while (WiFi.status() != WL_CONNECTED) delay(300);
 randomSeed(millis());
 Serial.println(fetchRandomItemTitle(RSS_URL, N));
}


void loop() {}
FabScene(ファブシーン)
Arduino IDEのモニタにニュースの見出しが表示された

起動時にWi-Fiへ接続し、RSSフィードを読み込んで見出しを抽出。まずはArduino IDEのシリアルモニタへ表示して動作を確認した。

ニュースはRSSの上位5件の中からランダムに1件を選択している。毎回同じ内容が表示されるわけではない「ゆらぎ」もこの装置の性格のひとつだ。

ChatGPTで日本語テキストをカタカナに変換

FabScene(ファブシーン)
デバイスに使用した「Grove – LCD RGB Backlight


情報のシンプルさを追求するため、表示装置にはGroveモジュールのキャラクタLCDを採用した。リッチな画像表示や漢字描画には対応しておらず、表示できる文字数も限られている。そのため、漢字混じりのニュース見出しをそのまま表示することはできない。

見出しをカタカナへ変換する方法としては、MeCabなどの形態素解析エンジンを使う選択肢がある。しかし、Raspberry Pi Picoで動かすにはリソースが不足する。MicroPython向けの日本語変換ライブラリも存在するが、Arduino環境を採用しているため適さなかった。

そこで発想を転換。Wi-Fiに接続できるのなら、変換処理をクラウドに任せればよい。今回はChatGPTで知られるOpenAIのAPIを利用し、ニュース見出しを半角カナへ変換する構成とした。

JSON処理の追加

OpenAI APIとやり取りするため、プログラムに #include <ArduinoJson.h> を追加し、JSON形式のデータを扱えるようにする。取得した見出しをJSONとして送信し、レスポンスJSONからテキスト部分だけを抜き出す関数を実装した。

// 見出しから改行などを削除
String jsonEscape(String s) {
 s.replace("\\", "\\\\");
 s.replace("\"", "\\\"");
 s.replace("\n", "\\n");
 s.replace("\r", "\\r");
 return s;
}


// Open AI からのレスポンスを変換
String parseResponsesOutputText(const String& json) {
 DynamicJsonDocument doc(12 * 1024);
 if (deserializeJson(doc, json)) return "";


 JsonVariant out = doc["output"];
 if (!out.is<JsonArray>()) return "";


 for (JsonObject item : out.as<JsonArray>()) {
   JsonVariant content = item["content"];
   if (!content.is<JsonArray>()) continue;


   for (JsonObject c : content.as<JsonArray>()) {
     const char* type = c["type"] | "";
     const char* text = c["text"] | "";
     if (strcmp(type, "output_text") == 0 && text[0]) return String(text);
     if (text[0]) return String(text);  // 保険
   }
 }
 return "";
}

APIキーの取得と課金

FabScene(ファブシーン)

Open AIのAPIプラットフォームを利用するには、アカウントを作成しAPIキーを発行する必要がある。ログイン後、「API Keys」から非公開鍵を作成し、安全な場所に保管しておこう。

FabScene(ファブシーン)

APIは利用量に応じてクレジットを消費するため、「Billing」から事前にチャージしておく。今回は5ドル(約780円)分を設定した。今回の用途では、1回あたり0.01円程度(※2026年2月時点の試算)と非常に低コストで運用できる。

見出しを半角カナに変換する

APIへ送信するプロンプトを含むコードも実装する。AIを利用するコードをAIに書いてもらったのだが、いかにも現代的な作り方だなぁと感慨深くなる。

// OpenAI: 見出し -> 半角カナ1行 追加
String convertToHalfKana(const String& headline) {
 WiFiClientSecure client;
 client.setInsecure();


 HTTPClient https;
 https.setTimeout(30000);


 if (!https.begin(client, "https://api.openai.com/v1/responses")) {
   return "OAI_BEGIN_FAIL";
 }


 https.addHeader("Content-Type", "application/json");
 https.addHeader("Authorization", String("Bearer ") + openai_api_key);


 String prompt =
   "次のニュース見出しを、読み仮名の半角カナに変換せよ。\n"
   "出力は1行のみ。説明・引用符・前置きは禁止。\n"
   "使ってよいのは ASCII と 半角カナ と半角スペースのみ。\n"
   "全角文字(カタカナ/ひらがな/漢字/、。・ー/全角数字)は禁止。\n"
   "見出し: "
   + headline;


 String payload = "{"
                  "\"model\":\"gpt-4.1-mini\","
                  "\"temperature\":0,"
                  "\"max_output_tokens\":80,"
                  "\"input\":\""
                  + jsonEscape(prompt) + "\""
                                         "}";


 int code = https.POST(payload);
 String res = https.getString();
 https.end();


 if (code != 200) return "OAI_HTTP_" + String(code);


 String out = parseResponsesOutputText(res);
 out.trim();
 int nl = out.indexOf('\n');
 if (nl >= 0) out = out.substring(0, nl);
 out.trim();


 return out.length() ? out : "PARSE_FAIL";
}

プロンプトでは、

  • 半角カナのみ
  • 1行のみ
  • 説明や引用符は禁止

と明示し、役物や全角文字を排除することで、LCD表示時のエラーを防いでいる。

FabScene(ファブシーン)

取得した見出しをAPIへ投げると、漢字混じりの文章が半角カナ一行へと変換されて返ってきた。たまに読みを間違うこともあるが、概ね問題なくカタカナに変換されていた。

筆者はOpenAIのAPIを初めて使ったが、仕組みを理解すれば難しくはない。チャット形式ではないものの、プロンプトを設計し、JSONで送信するだけだ。一連の流れを押さえれば、マイコンとクラウドAIを組み合わせた実装も十分現実的であると実感した。

※ここまでのプログラムはこちら

LCDに文字列を表示

最後に、カタカナへ変換した文字列をLCDへ表示する。

まずはライブラリ「Grove – LCD RGB Backlight」を追加する。Groveケーブル1本で制御できる便利なツールだ。#include “rgb_lcd.h” を追加し、rgb_lcd lcd; のようにインスタンスを宣言して利用する。

しかし、ここで問題が発生した。APIから返ってきた半角カナ文字列をそのまま lcd.print() に渡すと、文字化けしてしまうのである。

FabScene(ファブシーン)
カタカナのような何かが表示されているが…

原因は文字コードの違いにあった。プログラム側ではUTF-8で文字列を扱っているが、LCDは独自の文字コードテーブルを使用している。つまり、UTF-8の半角カナをそのまま送信しても、LCD側では正しく解釈されないのだ。

そこで、UTF-8文字列を1文字ずつ読み取り、LCD側のコードへ変換して書き込む処理を実装した。

// UTF-8を1文字ずつ読む関数
bool utf8Next(const String& s, int& i, uint32_t& cp) {
 if (i >= (int)s.length()) return false;
 uint8_t c = (uint8_t)s[i++];


 if (c < 0x80) {
   cp = c;
   return true;
 }


 if ((c & 0xE0) == 0xC0) {
   uint8_t c2 = (uint8_t)s[i++];
   cp = ((c & 0x1F) << 6) | (c2 & 0x3F);
   return true;
 }


 if ((c & 0xF0) == 0xE0) {
   uint8_t c2 = (uint8_t)s[i++];
   uint8_t c3 = (uint8_t)s[i++];
   cp = ((c & 0x0F) << 12) | ((c2 & 0x3F) << 6) | (c3 & 0x3F);
   return true;
 }


 return false;
}


// LCDへ1文字書く
static const uint8_t KANA_BASE = 0xA1;


void lcdWriteCp(uint32_t cp) {
 if (cp <= 0x7F) {
   lcd.write((uint8_t)cp);
   return;
 }


 if (cp >= 0xFF61 && cp <= 0xFF9F) {
   lcd.write(KANA_BASE + (cp - 0xFF61));
   return;
 }


 lcd.write(' ');
}


// 文字列を表示する関数
void lcdPrintUtf8(const String& s) {
 int i = 0;
 while (i < (int)s.length()) {
   uint32_t cp;
   if (!utf8Next(s, i, cp)) break;
   lcdWriteCp(cp);
 }
}

これで、lcd.print() の代わりに lcdPrintUtf8() を使えば、半角カタカナを正しく表示できるようになった。

FabScene(ファブシーン)
カタカナの表示に成功!

ここまでで、装置の基本構造は完成した。

  • RSSでニュース見出しを取得
  • OpenAI APIで半角カナへ変換
  • UTF-8を変換してLCDへ表示

それぞれは小さな処理だが、積み重ねることでひとつの装置になる。詰まる場面もあったが、ステップを分解して進めれば確実に形になる。少しずつ完成へ近づいていく過程そのものが、工作の醍醐味だ。

※ここまでのプログラムはこちら

コンパクトで取り回しやすい拡張基板

FabScene(ファブシーン)
拡張ボードにRaspberry Pi Pico WHを載せたところ
FabScene(ファブシーン)
単3電池を入れ、Groveモジュールを追加してもコンパクトな仕上がり

ソフトウェアが完成したら、次はハードウェアの仕上げである。

拡張ボードには4か所のビス止め用穴が用意されている。この位置を基準に、3Dプリントで台座を設計した。リセットボタンや電源スイッチといった操作部を塞がないよう、指が自然に入るクリアランスを確保している。

FabScene(ファブシーン)

電源スライドスイッチとリセットスイッチはいずれもアクセスしやすい位置にあり、PCを接続しなくても簡単に再起動や検証が行える。実際に使ってみると、この「当たり前に触れる」設計が思いのほか快適だ。

FabScene(ファブシーン)

最終的には、LCDに表示しきれない文字列へスクロール処理を追加し、さらに新聞モチーフに合わせてNTPから正確な時刻を取得する機能も組み込んだ。こうして、起動時および1分ごとにニュースを取得し、見出しを表示する一連のプログラムが完成した。

ランダム表示のため、直前と同じニュースが続くこともある。しかし、それも含めてこの装置の「ゆらぎ」である。慌ただしい情報の洪水から少し距離を取りながら、ゆるやかにニュースと付き合ってほしい。

※ここまでのプログラムはこちら

まとめ

FabScene(ファブシーン)

Raspberry Pi Picoを使った工作の魅力は、やはりそのコンパクトさにある。「AD2040AA Raspberry Pi Pico 単3電池拡張ボード」は、乾電池駆動やGroveコネクタなどの拡張性によって、使いやすさとコンパクトさを両立した開発を後押ししてくれた。

GPIOをそのまま活用できるため、センサーを接続して電源の取り回しが難しい場所で計測するといった用途にも応用できる。実際に、筐体へ収めて自然環境モニタリングを行う事例も公開されており、アイデア次第でさまざまな展開ができるだろう。

今回制作した「一行新聞」を参考に、皆さんならではのRaspberry Pi Pico工作やプロジェクトを楽しんでもらえたら幸いだ。

ビット・トレード・ワン × FabScene コラボ記念クーポンのご案内

ビット・トレード・ワンとのタイアップ記念として、同社公式サイトの商品が全品10%OFFになるクーポンのご紹介です。

決済画面からクーポンコード欄にfabsceneと入力してください。
こちらのリンクから公式ストアにアクセスしても、全品10%OFFの割引が適応されます。

もちろん、今回紹介した製品も割引対象です。ぜひご利用下さい!

本記事はSponsored記事です。
提供:株式会社ビット・トレード・ワン

fabsceneの更新情報はXで配信中です

この記事の感想・意見をSNSで共有しよう
  • URLをコピーしました!

ライター/編集者。大学で3Dプリンターと出会いものづくりの面白さに目覚め、デジタルファブリケーションの世界へ。卒業後、研究員として2年半ほど従事したのち、ものづくりを中心としたライターとして独立。
2023年には墨田区でファブ施設「京島共同凸工所」の運営をスタート。文章と場づくりを行き来しながら、街での生活を満喫している。工房での日々を綴った自主制作本「京島の十月」が販売中。

目次