Raspberry Pi でWiringPi 2025

C言語でRaspberry Piのプログラムをする際に欠かせなかったWiringPiライブラリですが、サポートの休止などもあって違うライブラリへの変更をされた方も多いかもしれません。しかしながら、代替手段も不安定で混乱した例も見受けられます。

そもそも、Raspberry Piのプログラム開発がPython推奨となり、C言語での開発が、あまり良いとは言えない選択枝となったのかもしれません。

でも、本当にWiringPiのサポートが休止されているのかと調べると、提供されていました。

1.WiringPiのインストール

上記サイトからWiringPiアーカイブをダウンロードして、Raspberry Piのユーザフォルダなどで解凍します。インストール方法は、以前と同じく

./build

を実行します。環境によっては権限がなかったりするかもしれないので、権限を付加してから実行してください。

今のWiringPiライブラリはRaspberry Pi 5もサポートされていますが、制限事項として

Support: WiringPi supports all Raspberry Pi Boards including Pi 5 ( 🚧 On the Pi 5, only the GCLK functionality is currently not supported due to missing documentation of the RP1 chip).

https://github.com/WiringPi/WiringPi

とあります。GCLK機能が良く分かりませんが、そもそもRaspberry Pi 5では電子工作にはあまり使われないと思うので、WiringPiライブラリの使用は問題ないと思われます。

2.新しいWiringPiでビルドしてみる

新しいWiringPiライブラリですが、以前提供されていた関数が使えなくなっていることを確認しました。Raspberry Pi Zeroで以前提供していたGPSプログラムでは

gps_oled.c:200:9: warning: implicit declaration of function ‘waitForInterrupt’; did you mean ‘waitForInterrupt2’? [-Wimplicit-function-declaration]
  200 |         waitForInterrupt(INTERRUPT_PIN, 2000) ;

となり、新しく提供された「waitForInterrupt2」を使わなければならないようです。

しかしながら、若干の修正で済みそうです。そこで、新しいWiringPiライブラリでプログラムを作成してみます。(今回はwaitForInterrupt2を使いません)

3.前回投稿したPIGPIOライブラリのプログラムをWiringPiで作成してみた

前回、PIGPIOライブラリを使うべきという投稿をしたのですが、やっぱりWiringPiを使いたいというプログラムを作ってみました。

というのも、今回はI2C接続の0.96インチOLEDを使用しており、このOLEDを動作させるライブラリがWiringPiライブラリを使っているのです。自分が使わなくても使用するライブラリの方で使われている現実があり、できればWiringPiライブラリを使いたい希望がありました。

Raspberry Piに上記ライブラリをインストールしておいてください。

4.ハードウェアの接続

使用するハードウェア、Raspberry Pi Zero 2W、GPSユニット、OLED、タクトスイッチの接続方法を以下に示します。

created by Rinker
Raspberry Pi
¥3,900 (2025/07/11 10:03:03時点 Amazon調べ-詳細)

Raspberry Pi Zero 2WとGPSユニット

GPSユニットとRaspberry Pi Zero 2Wは、以下のように接続します。左がGPSユニット、右がRaspberry Pi Zero 2Wです。

Vc -> 5V出力
GND -> GND
TX -> GP15 (RX)
RX -> GP14 (TX)
PPS -> 使わない LEDを光らせるのに使うと良いかも
EN -> 3.3V

Raspberry Pi Zeroと0.96インチOLED

0.96インチOLEDとRaspberry Pi Zero 2Wは、以下のように接続します。左が0.96インチOLED、右がRaspberry Pi Zero 2Wです。

GND -> GND
VCC -> 5V
SCL -> GP03
SDA -> GP02

Raspberry Pi Zeroとタクトスイッチ

created by Rinker
KKHMF
¥599 (2025/07/11 14:02:21時点 Amazon調べ-詳細)

プログラムの終了スイッチをGPIO26に割り当てました。

5.プログラム

以下、ソースを用意しましたが、従来のソースを引用しているため、余分な部分もあります。ご了承ください。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#include <wiringPi.h>
#include <wiringSerial.h>
#include <string.h>
#include <unistd.h>

#include "./oled1306lib/ssd1306_i2c.h"

#define SERIAL_PORT "/dev/serial0"
#define ONE_LINE 1024

#define ENDSWITCH 26

int g_fd ;
char g_oneline[ONE_LINE] ;

char g_ido[128] ;
char g_keido[128] ;
float g_speed ;
char g_speedchr[16] ;
int g_satelites = 0 ;
char g_satelitesChar[3] ;
float g_altitude ;
char g_altitudechr[16] ;

static char *g_monthName[] = {"JAN", "FEB", "MAR", "APL", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"};

#define HEADER1 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
#define HEADER2 "<gpx xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"http://www.topografix.com/GPX/1/0\" xsi:schemaLocation=\"http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd\">\n"
#define HEADER3 "<trk><name>RASPI LOG</name>\n"
#define HEADER4 "<number>1</number>\n"
#define HEADER5 "<trkseg>\n"
#define FOOTER "</trkseg></trk>\n"
#define FOOTER2 "</gpx>\n"

float g_gpxLAT ;
float g_gpxLOT ;

char g_logbuff[512];
char g_nengappi[32] ;
char g_jihunbyou[32] ;

FILE *g_fp;

void MySplit() {
	int counter = 0;
	int onelineLength = strlen(g_oneline) ;
	char buff[256] ;
	int point = 0 ;
	float value = 0 ;

	int _do ;
	int _hun ;
	int _byou ;
	float amari ;

	memset(buff, 0x00, 256) ;
	for (int i=0;i<onelineLength;i++) {
		if (g_oneline[i] == ',') {
			switch(point) {
			case 2 :
				// ido
				value = atof(buff) ;
				_do = (int)(value / 100) ;
				_hun = (int)(value - (_do * 100)) ;
				amari = value - _do * 100 - _hun ;
				_byou = (int)(amari * 60) ;
				amari = amari * 60 - (float)_byou ;
				sprintf(g_ido, "%03d'%02d'%02d'%1d", _do, _hun, _byou, (int)(amari * (float)10)) ;
				g_gpxLAT = (float)_do + (float)((float)((value - (_do * 100)) / (float)60)) ;

				break ;
			case 4 :
				// keido
				value = atof(buff) ;
				_do = (int)(value / 100) ;
				_hun = (int)(value - (_do * 100)) ;
				amari = value - _do * 100 - _hun ;
				_byou = (int)(amari * 60) ;
				amari = amari * 60 - (float)_byou ;
				sprintf(g_keido, "%03d'%02d'%02d'%1d", _do, _hun, _byou, (int)(amari * (float)10)) ;
				g_gpxLOT = (float)_do + (float)((float)((value - (_do * 100)) / (float)60)) ;

				break ;

			case 7 :
				// satelites
				g_satelites = atoi(buff) ;
				sprintf(g_satelitesChar, "%02d", g_satelites) ;
				
				break ; 

			case 9 :
				// altitude
				g_altitude = atof(buff) ;
				sprintf(g_altitudechr, "%4.1f", (g_altitude + 0.5)) ;
				
				break ; 

			default :
				break ;
			}
			point ++ ;
			memset(buff, 0x00, 256) ;
			counter = 0 ;
			
		} else {
			buff[counter] = g_oneline[i] ;
			counter ++ ; 
		}
	}


}

void MySplit2() {
	int counter = 0;
	int onelineLength = strlen(g_oneline) ;
	char buff[256] ;
	int point = 0 ;
	float value = 0 ;

	memset(buff, 0x00, 256) ;
	for (int i=0;i<onelineLength;i++) {
		if (g_oneline[i] == ',') {
			switch(point) {
			case 7 :
				// speed
				value = atof(buff) ;

				g_speed = value ;
				sprintf(g_speedchr, "%03d", (int)(value + 0.5)) ;

				break ;
			
			default :
				break ;
			}
			point ++ ;
			memset(buff, 0x00, 256) ;
			counter = 0 ;
			
		} else {
			buff[counter] = g_oneline[i] ;
			counter ++ ; 
		}
	}
}

void nmea0183analysis() {
	float direction ;
	float speed ;

	if (strncmp(g_oneline, "$GPGGA", 6) == 0) {
		MySplit() ;
	    sprintf(g_logbuff, "<trkpt lat=\"%f\" lon=\"%f\"><ele>%s</ele><time>%s%s</time></trkpt>\n", g_gpxLAT, g_gpxLOT, g_altitudechr, g_nengappi, g_jihunbyou) ;
	    if (g_fp != NULL && (int)g_gpxLAT != 0) {
	      fputs(g_logbuff, g_fp);
	    }

	} else if (strncmp(g_oneline, "$GPVTG", 6) == 0) {
		MySplit2() ;
	}

}

void GetJikoku() {
    time_t current = time(NULL) ; 

    struct tm* timer = gmtime(&current);

    sprintf(g_nengappi, "%04d-%02d-%02dT", 2000+(timer->tm_year-100), (timer->tm_mon+1), timer->tm_mday) ;
    sprintf(g_jihunbyou, "%02d:%02d:%02dZ", timer->tm_hour, timer->tm_min, timer->tm_sec) ;

}

void get_gps_data() {
	char buff[1024] ;
	int read_length ;
	int onelinepointer ;

	onelinepointer = strlen(g_oneline) ;

	read_length = serialDataAvail(g_fd) ;

	for (int i=0;i<read_length;i++) {
		if (i+onelinepointer >= ONE_LINE) {
			break ;
		}
		g_oneline[onelinepointer] = serialGetchar(g_fd) ;
		if (g_oneline[onelinepointer] == 0x0a) { 
			GetJikoku();
			nmea0183analysis() ;

			printf(g_oneline) ;
			memset(g_oneline, 0x00, ONE_LINE) ;
			onelinepointer = 0 ;
			
		} else {
			onelinepointer++ ;
		}
	}

}

int init() {

	if (wiringPiSetupGpio() == -1) {
		printf("wiringPi setup error") ;
		return -1 ;
	}

	g_fd = serialOpen(SERIAL_PORT, 9600); 

	if (g_fd < 0) {
		printf("serial open error") ;
		return -1 ;
	}

	// logfile
	time_t current = time(NULL) + 9 * 60 * 60 ;
	struct tm* timer = gmtime(&current);

	sprintf(g_logbuff, "%04d%02d%02d%02d%02d%02d.gpx", 2000+(timer->tm_year-100), (timer->tm_mon+1), timer->tm_mday,
			timer->tm_hour, timer->tm_min, timer->tm_sec) ;

	if ((g_fp = fopen(g_logbuff, "w")) == NULL) {
		printf("logfile open error\n") ;
		return 1 ;
	  }

	    // GPX Header
	fputs(HEADER1, g_fp);
	fputs(HEADER2, g_fp);
	fputs(HEADER3, g_fp);
	fputs(HEADER4, g_fp);
	fputs(HEADER5, g_fp);

	memset(g_oneline, 0x00, ONE_LINE);

	// OLED
	ssd1306_begin(SSD1306_SWITCHCAPVCC, SSD1306_I2C_ADDRESS);
	ssd1306_clearDisplay();

	// 終了スイッチ
	pullUpDnControl(ENDSWITCH, PUD_UP) ;
	
	return 1 ;
}

int main(void) {
	int readdata = 0 ;
	char buff[64] ;

	if (init() == -1) {
		return 1 ;
	}
	
	while(1) {
		readdata = digitalRead(ENDSWITCH) ;
		if (readdata == 0) {
			break ;
		}

		get_gps_data() ;

		ssd1306_clearDisplay();
		ssd1306_setTextSize(1) ;
		
		sprintf(buff, "ido:%s\nkeido:%s", g_ido, g_keido) ;
		ssd1306_drawString(buff);
		printf(buff) ;

		ssd1306_display();

		sleep(1) ;

	}

	fputs(FOOTER, g_fp);
	fputs(FOOTER2, g_fp);
	fclose(g_fp) ;
 
	serialClose(g_fd) ;

	return 1 ;
}

初期のソースがそのままでリファクタリングしなければならないところですが、参考程度にと載せてみました。

ビルドは、以下のコマンドで行います。OLEDライブラリへのパスは環境に合わせて修正してください。(ソース名はgps_oled2wですが、任意です)

gcc gps_oled2w.c ./oled1306lib/ssd1306_i2c.c -lwiringPi -o gps_oled2w

以下のコマンドで実行します。

sudo ./gps_oled2w

プログラムが実行されると、ターミナルにはNMEA-0183出力が表示され、GPSが有効になると0.96インチOLEDに緯度・経度が表示されます。また、プログラムの実行フォルダに、GPX形式のログが出力されます。

OSで動くGPSロガーは使い勝手が悪いですが、マイコンで同じことをするとタイミングの関係だと思うのですが動作が安定しない現象に悩まされており、やっぱりRaspberry Piがいいのかなと思い始めています。この記事の前にPICOネタが2つあったのですが、いまだに保留になっています。

今回は実用性を考慮し、ケースに入れてパッケージング化しました。GPSユニットが外になったのは、中に入れるとノイズのせいか感度が悪くなるからです。せっかく感度の良いGPSユニットを使っているのですから、ノイズ対策を万全にしたいのですが、やり方が良く分かりません。

また、あくまでもGPX形式ログを取ることを目的としているため、OLEDの表示は最小限です。どちらにしろ良く見えないので、これで良いかと思います。プログラムをもう少しイレギュラー対策しないと実用では耐えられないと思うので、この後対策しようと思います。今回のパッケージは、アウトドアでのGPSログ取得に使用します。

6.WiringPiライブラリも継続して欲しい

C言語でRaspberry Piのプログラムを作成する情報はWiringPiライブラリを使う場合が多く、内容も古くなっています。現在はPythonでプログラムをする場合が多いため、C言語を使う新しい情報が少ないです。

私はUNIX/C言語の組み合わせで仕事をしてきた人なので、もうPythonやKotlinといった今の開発にはついていけません。Raspberry Pi のC言語環境が続くことを祈るばかりです。

WiringPiライブラリには、今後も期待したいですね。