ATOMcam2の画像から流星を録画

ATOMcam2をベランダへ設置し、RTSP
で画像を流す。
小規模な火球をキャッチ(左上)。動画を拡大すると、画面上部中央に、おおぐま座(北斗七星)の一部が写っている。

処理は、Python(jupyter notebook)のスクリプト。cv2の中の移動検知のライブラリーを利用している。流星の他に、航空機、人工衛星、移動が激しい雲、鳥なども記録されるので、何らかの方法で、フィルタリングしたい。

とりあえず、一定のサイズ以下のファイルを削除するスクリプト(500kBの例)

find . -name "*.avi" -type 'f' -size -500k -delete
import cv2
import numpy as np
import time
import datetime
import os

capture = cv2.VideoCapture('rtsp://4190:2712@192.168.68.74/live')
#capture=cv2.VideoCapture(0)
PATH="/media/mars/ff2880cc-1a99-40bd-88c1-5cdc86fe9eed/home/mars/DATA"
W = capture.get(cv2.CAP_PROP_FRAME_WIDTH)
H = capture.get(cv2.CAP_PROP_FRAME_HEIGHT)
W2=int(W/2)
H2=int(H/2)
fourcc = cv2.VideoWriter_fourcc(*"XVID")
print(W,H)

def key(k):
    global th, tc,track,reverse
    if k == ord('2'):
        th = th - 1
    elif k == ord('3'):
        th = th + 1
    elif k == ord('4'):
        tc = tc -5
    elif k == ord('5'):
        tc = tc +5
    elif k == ord('t'):
        track = not track
    elif k == ord('r'):
        reverse = not reverse

fontFace =cv2.FONT_HERSHEY_SIMPLEX
track, reverse = False,False
avg=None
th = 10
tc = 25
x,y=0,0
writer = None
time_start = time.time()
frame=0
while(True):
    ret, img = capture.read()
    org = img.copy()
    #img = cv2.resize(im, dsize=(W2, H2))
    img=img[0:int(H*0.9),0:int(W)]    # 映像の下側10%を検知範囲から除外。
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    if reverse:
        gray=cv2.bitwise_not(gray)
        avg=cv2.bitwise_not(avg)
    if avg is None:
        avg = gray.copy().astype("float")
        continue

    cv2.accumulateWeighted(gray, avg, 0.5)
    frameDelta = cv2.absdiff(gray, cv2.convertScaleAbs(avg))
    thresh = cv2.threshold(frameDelta, th, 255, cv2.THRESH_BINARY)[1]
    contours,hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    detect=False
    for i in range(0,len(contours)):
        if len(contours[i]) > 0:
             if cv2.contourArea(contours[i]) > tc:
                detect=True
                time_start = time.time()
                if writer is None and track:
                    now=datetime.datetime.today()
                    date=now.strftime("%Y%m%d")
                    cDIR=PATH+'/'+date
                    if not(os.path.exists(cDIR)):
                           os.mkdir(cDIR)
                    fname=cDIR+'/'+'E-'+now.strftime("%Y%m%d_%H:%M:%S")+".avi"
                    writer = cv2.VideoWriter(fname, fourcc, 15, (int(W), int(H)))
                rect = contours[i]
                x, y, w, h = cv2.boundingRect(rect)

                cv2.rectangle(img, (x-w, y-h), (x + w*2, y + h*2), (0, 0, 255), 2)
                
    if time.time() -  time_start  > 5:
        if writer is not None:
            writer.release()
            #f.close()
            frame=0
            writer = None
    now=datetime.datetime.today()    
    text=now.strftime("%Y/%m/%d %H:%M:%S")+' No:'+str(frame)+ ' '+" TH:"+str(th)+" SZ:"+str(tc)
    img = cv2.putText(img, text, (30,50), fontFace,1,color=(0, 255, 0))
    org = cv2.putText(org, text, (30,50), fontFace,1,color=(0, 255, 0))
    text1="REC:"+str(track) + "  reverse:" + str(reverse)
    if writer is not None:
        frame=frame+1
        text1=fname+' '+text1
    img = cv2.putText(img, text1, (30,80), fontFace,1,color=(0, 255, 0))
    #cv2.imshow('thresh-level',thresh)
    cv2.imshow("IMAGE",img)
    if writer is not None:
        writer.write(org)
        
    k=cv2.waitKey(1) & 0xFF
    key(k)
    if k== ord('q'):
        break

capture.release()
if writer is not None:
    writer.release()
cv2.destroyAllWindows()

Thread機能を利用して、処理の効率化を図る。

import cv2
import threading
import queue
import numpy as np
import time
import datetime
import os

cPATH='rtsp://4190:2712@192.168.68.74/live'
#cPATH= 'rtsp://admin:@192.168.68.128:554/1/h264major'
PATH="/home/pi/DATA"

fourcc = cv2.VideoWriter_fourcc(*"XVID")

class ThreadingVideoCapture:
    def __init__(self, src, max_queue_size=256):
        self.video = cv2.VideoCapture(src)
        self.q = queue.Queue(maxsize=max_queue_size)
        self.stopped = False

    def start(self):
        thread = threading.Thread(target=self.update, daemon=True)
        thread.start()
        return self

    def update(self):
        while True:
            if self.stopped:
                return
            if not self.q.full():
                ok, frame = self.video.read()
                self.q.put((ok, frame))
                if not ok:
                    self.stop()
                    return

    def read(self):
        return self.q.get()

    def stop(self):
        self.stopped = True

    def release(self):
        self.stopped = True
        self.video.release()

    def isOpened(self):
        return self.video.isOpened()

    def get(self, i):
        return self.video.get(i)
    
def key(k):
    global th, tc,track,reverse,disp
    if k == ord('2'):
        th = th - 1
    elif k == ord('3'):
        th = th + 1
    elif k == ord('4'):
        tc = tc -5
    elif k == ord('5'):
        tc = tc +5
    elif k == ord('t'):
        track = not track
    elif k == ord('d'):
        disp= not disp
    elif k == ord('r'):
        reverse = not reverse

def detect_mov(contours,detect):
    for i in range(0,len(contours)):
            if len(contours[i]) > 0:
                 if cv2.contourArea(contours[i]) > tc:
                    detect=detect+1
                    rect = contours[i]
                    x, y, w, h = cv2.boundingRect(rect)
                    cv2.rectangle(img, (x-w, y-h), (x + w*2, y + h*2), (0, 0, 255), 3)
    return img,detect
                    
fontFace =cv2.FONT_HERSHEY_SIMPLEX

video = ThreadingVideoCapture(cPATH)
video.start()
if not video.isOpened():
    raise RuntimeError

W = video.get(cv2.CAP_PROP_FRAME_WIDTH)
H = video.get(cv2.CAP_PROP_FRAME_HEIGHT)
W2,H2=int(W/2),int(H/2)
cv2.namedWindow('ATOM', cv2.WINDOW_AUTOSIZE)

track, reverse,disp = False,False,False
avg=None
th = 10
tc = 25
x,y=0,0
writer = None
time_start = time.time()
frame=0
fname=None
log=PATH+'/metro.log'
detect=0
while(True):
    ret, img = video.read()
    if ret:
        org = img.copy()
        img = cv2.resize(img, dsize=(W2,H2))
        img=img[0:int(H2*0.85),0:int(W2)]
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        if reverse:
            gray=cv2.bitwise_not(gray)
            avg=cv2.bitwise_not(avg)
        if avg is None:
            avg = gray.copy().astype("float")
            continue

        cv2.accumulateWeighted(gray, avg, 0.5)
        frameDelta = cv2.absdiff(gray, cv2.convertScaleAbs(avg))
        thresh = cv2.threshold(frameDelta, th, 255, cv2.THRESH_BINARY)[1]

        contours,hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
        img,detect=detect_mov(contours,detect)
        if writer is None and track and detect !=0:
            time_start = time.time()
            now=datetime.datetime.today()
            date=now.strftime("%Y%m%d")
            cDIR=PATH+'/'+date
            if not(os.path.exists(cDIR)):
                os.mkdir(cDIR)
            fname=cDIR+'/'+'E-'+now .strftime("%Y%m%d_%H%M%S")+".avi"
            writer = cv2.VideoWriter(fname, fourcc, 15, (int(W), int(H)))
            
        if time.time() -  time_start  > 5:
            if writer is not None:
                writer.release()
                #f.close()
                frame=0
                writer = None
        now=datetime.datetime.today()    
        text=now.strftime("%Y%m%d %H%M%S")+' No:'+str(frame)+ ' '+" TH:"+str(th)+" SZ:"+str(tc)
        img = cv2.putText(img, text, (30,50), fontFace,1,color=(0, 255, 0))
        org = cv2.putText(org, text, (30,50), fontFace,1,color=(0, 255, 0))
        text1="REC:"+str(track) + "  reverse:" + str(reverse)
        if writer is not None:
            frame=frame+1
            text1=fname+' '+text1
        img = cv2.putText(img, text1, (30,80), fontFace,1,color=(0, 255, 0))
        #cv2.imshow('thresh-level',thresh)
        if disp:
            cv2.imshow("ATOM",img)
        
        if writer is not None:
            writer.write(org)
    else:
        now=datetime.datetime.today()
        date=now.strftime("%Y%m%d_%H%M%S")
        print("disconected:",date)
        video.release()
        avg = None
        video = ThreadingVideoCapture(cPATH)
        video.start()
    k=cv2.waitKey(int(1000 / 30)) & 0xFF
    key(k)
    if k== ord('q'):
        break

video.release()
if writer is not None:
    writer.release()
cv2.destroyAllWindows()
print('Done.')

2重星団

ペルセウス座にある散開星団。カシオペヤ座との境界の近く、天の川のほぼ中央に位置している。2つの散開星団が近接しているためこの名で呼ばれる。NGCカタログ/Melカタログでのカタログ番号は、西側の星団が NGC 869/ Mel 13、東側の星団が NGC 884/ Mel 14。

ネットワークカメラ ATOM Cam2で遊ぶ

ATOM Cam2は、防水となっていて屋外にも設置できそうなので、流星の録画・観測向きかもしれない。

こちらのブログを参照して必要なファイルを録画用のSDカードへ配置するだけで、telnet/ftp/rtspを利用できる。

配置の手順などは、こちらのgithubに

ファイルを配置してカメラをrebootし、とりあえずnmapでOS,サービスの情報を表示してみる。

$ sudo nmap -O 192.168.68.74
Starting Nmap 7.70 ( https://nmap.org ) at 2021-10-16 21:05 JST
Nmap scan report for 192.168.68.74
Host is up (0.010s latency).
Not shown: 998 closed ports
PORT   STATE SERVICE
21/tcp open  ftp
23/tcp open  telnet
MAC Address: 7C:DD:E9:01:F0:F1 (Atom Tech)
Device type: general purpose
Running: Linux 2.6.X|3.X
OS CPE: cpe:/o:linux:linux_kernel:2.6 cpe:/o:linux:linux_kernel:3
OS details: Linux 2.6.32 - 3.10
Network Distance: 1 hop

$ nmap -A 192.168.68.74
Starting Nmap 7.70 ( https://nmap.org ) at 2021-10-16 21:04 JST
Nmap scan report for 192.168.68.74
Host is up (0.013s latency).
Not shown: 998 closed ports
PORT   STATE SERVICE VERSION
21/tcp open  ftp     BusyBox ftpd (D-Link DCS-932L IP-Cam camera)
23/tcp open  telnet  BusyBox telnetd
Service Info: Host: Ingenic; Device: webcam; CPE: cpe:/h:dlink:dcs-932l

RTSPらしいポートが見えないが、説明によると8554/TCPらしいのでポートを指定して、再びnmap。

$ sudo nmap -p8554 -A 192.168.68.74
Starting Nmap 7.70 ( https://nmap.org ) at 2021-10-16 21:25 JST
Nmap scan report for 192.168.68.74
Host is up (0.0036s latency).

PORT     STATE SERVICE VERSION
8554/tcp open  rtsp    DoorBird video doorbell rtspd
|_rtsp-methods: OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE, GET_PARAMETER, SET_PARAMETER
MAC Address: 7C:DD:E9:01:F0:F1 (Atom Tech)
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
Device type: general purpose
Running: Linux 2.6.X|3.X
OS CPE: cpe:/o:linux:linux_kernel:2.6 cpe:/o:linux:linux_kernel:3
OS details: Linux 2.6.32 - 3.10
Network Distance: 1 hop
Service Info: Device: webcam

ということで、8554/TCPが空いている.

VLCのネットワークストリームを開き、 rtsp://[IPアドレス]:8554/unicast で無事動画を表示することができた。

ブログによると8080/TCPでWebアクセスできて、詳細な設定が可能ということだが、ATOM Cam2のバージョンが違うのか、8080/TCPは開いていないようだ。

ハックの新バージョンが公開されていました。新バージョンで上書き設置し、nmapを実行した結果は、次のとおりでした。ブラウザで http://[IPアドレス]:8080/cgi-bin/honeylab.cgi を開くと、設定画面やステータスが表示され、ftpとrtspの有効化(ON/OFF)が可能となりました。初期状態ではftp/rtspが無効化されている。

PORT     STATE SERVICE
23/tcp   open  telnet
8080/tcp open  http-proxy
9999/tcp open  abyss

telnetでログインして(root/atomcam2) freeコマンドでメモリー容量を確認したら75Kバイトのようだ

 telnet 192.168.68.74
Trying 192.168.68.74...
Connected to 192.168.68.74.
Escape character is '^]'.

Ingenic login: root
Password:

[root@Ingenic:~]# free
             total         used         free       shared      buffers
Mem:         75084        55300        19784            0          200
-/+ buffers:              55100        19984
Swap:            0            0            0

# df -h
Filesystem                Size      Used Available Use% Mounted on
/dev/root                 3.6M      3.6M         0 100% /
tmpfs                    36.7M      8.0K     36.7M   0% /dev
tmpfs                    36.7M      9.8M     26.9M  27% /tmp
tmpfs                    36.7M      4.0K     36.7M   0% /run
media                    36.7M         0     36.7M   0% /media
/dev/mtdblock3            3.6M      3.6M         0 100% /system
/dev/mtdblock6          384.0K    120.0K    264.0K  31% /configs
/dev/mmcblk0p1           29.1G      1.1G     28.0G   4% /media/mmc
/dev/mmcblk0p1           29.1G      1.1G     28.0G   4% /tmp/mmc
tmpfs                    36.7M      9.8M     26.9M  27% /bin/busybox
tmpfs                    36.7M      9.8M     26.9M  27% /bin
/dev/loop0               45.2M     40.1M      2.0M  95% /tmp/newroot
tmpfs                    36.7M      9.8M     26.9M  27% /etc/passwd
tmpfs                    36.7M      9.8M     26.9M  27% /etc/shadow
/dev/loop0               45.2M     40.1M      2.0M  95% /tmp/newroot/mnt/usr/lib
/dev/loop0               45.2M     40.1M      2.0M  95% /usr
/dev/loop0               45.2M     40.1M      2.0M  95% /usr/lib

参照したブログによると、独自のカーネルで起動することも可能らしい。今後の展開が楽しみだ。

センサーのデータをinfluxdbへ送る

python版

参考URL(ラズパイ+BME280で温度・湿度・気圧を計測(BMP280は湿度なし)

次のpythonコードをcronで定期的(例えば3分毎)に実行し、influxdbのホストへ送信する。

#coding: utf-8
#encoding: utf-8

from influxdb import InfluxDBClient
client = InfluxDBClient(host='xxx.xxx.xxx.xxx', port=8086, username='****', password='****', database='iot01')
measurement = 'air5'
tags = {'place': 'leaf-bme280','host': 'RockPi4'}

from smbus2 import SMBus
import time

bus_number  = 7 # RockPi4の場合。 Raspiでは 1
i2c_address = 0x76

bus = SMBus(bus_number)

digT = []
digP = []
digH = []

t_fine = 0.0

def writeReg(reg_address, data):
        bus.write_byte_data(i2c_address,reg_address,data)

def get_calib_param():
        calib = []

        for i in range (0x88,0x88+24):
                calib.append(bus.read_byte_data(i2c_address,i))
        calib.append(bus.read_byte_data(i2c_address,0xA1))
        for i in range (0xE1,0xE1+7):
                calib.append(bus.read_byte_data(i2c_address,i))

        digT.append((calib[1] << 8) | calib[0])
        digT.append((calib[3] << 8) | calib[2])
        digT.append((calib[5] << 8) | calib[4])
        digP.append((calib[7] << 8) | calib[6])
        digP.append((calib[9] << 8) | calib[8])
        digP.append((calib[11]<< 8) | calib[10])
        digP.append((calib[13]<< 8) | calib[12])
        digP.append((calib[15]<< 8) | calib[14])
        digP.append((calib[17]<< 8) | calib[16])
        digP.append((calib[19]<< 8) | calib[18])
        digP.append((calib[21]<< 8) | calib[20])
        digP.append((calib[23]<< 8) | calib[22])
        digH.append( calib[24] )
        digH.append((calib[26]<< 8) | calib[25])
        digH.append( calib[27] )
        digH.append((calib[28]<< 4) | (0x0F & calib[29]))
        digH.append((calib[30]<< 4) | ((calib[29] >> 4) & 0x0F))
        digH.append( calib[31] )

        for i in range(1,2):
                if digT[i] & 0x8000:
                        digT[i] = (-digT[i] ^ 0xFFFF) + 1

        for i in range(1,8):
                if digP[i] & 0x8000:
                        digP[i] = (-digP[i] ^ 0xFFFF) + 1

        for i in range(0,6):
                if digH[i] & 0x8000:
                        digH[i] = (-digH[i] ^ 0xFFFF) + 1

def readData():
        data = []
        for i in range (0xF7, 0xF7+8):
                data.append(bus.read_byte_data(i2c_address,i))
        pres_raw = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4)
        temp_raw = (data[3] << 12) | (data[4] << 4) | (data[5] >> 4)
        hum_raw  = (data[6] << 8)  |  data[7]

        temp = compensate_T(temp_raw)
        pres = compensate_P(pres_raw)
        humi = compensate_H(hum_raw)
        print (round(temp,2))
        print (round(pres,2))
        print (round(humi,2))
        json_body = [
                {
                        'measurement': measurement,
                        'tags': tags,
                        'fields': {'temp': round(temp,2) , 'humi': round(humi,2) , 'hpa': round(pres,2)}
                }
        ]
        client.write_points(json_body)
        print(json_body)


def compensate_P(adc_P):
        global  t_fine
        pressure = 0.0

        v1 = (t_fine / 2.0) - 64000.0
        v2 = (((v1 / 4.0) * (v1 / 4.0)) / 2048) * digP[5]
        v2 = v2 + ((v1 * digP[4]) * 2.0)
        v2 = (v2 / 4.0) + (digP[3] * 65536.0)
        v1 = (((digP[2] * (((v1 / 4.0) * (v1 / 4.0)) / 8192)) / 8)  + ((digP[1] * v1) / 2.0)) / 262144
        v1 = ((32768 + v1) * digP[0]) / 32768

        if v1 == 0:
                return 0
        pressure = ((1048576 - adc_P) - (v2 / 4096)) * 3125
        if pressure < 0x80000000:
                pressure = (pressure * 2.0) / v1
        else:
                pressure = (pressure / v1) * 2
        v1 = (digP[8] * (((pressure / 8.0) * (pressure / 8.0)) / 8192.0)) / 4096
        v2 = ((pressure / 4.0) * digP[7]) / 8192.0
        pressure = pressure + ((v1 + v2 + digP[6]) / 16.0)

        return (pressure/100)
        #print "pressure : %7.2f hPa" % (pressure/100)

def compensate_T(adc_T):
        global t_fine
        v1 = (adc_T / 16384.0 - digT[0] / 1024.0) * digT[1]
        v2 = (adc_T / 131072.0 - digT[0] / 8192.0) * (adc_T / 131072.0 - digT[0] / 8192.0) * digT[2]
        t_fine = v1 + v2
        temperature = t_fine / 5120.0

        return temperature
        #print "temp : %-6.2f ℃" % (temperature)

def compensate_H(adc_H):
        global t_fine
        var_h = t_fine - 76800.0
        if var_h != 0:
                var_h = (adc_H - (digH[3] * 64.0 + digH[4]/16384.0 * var_h)) * (digH[1] / 65536.0 * (1.0 + digH[5] / 67108864.0 * var_h * (1.0 + digH[2] / 67108864.0 * var_h)))
        else:
                return 0
        var_h = var_h * (1.0 - digH[0] * var_h / 524288.0)
        if var_h > 100.0:
                var_h = 100.0
        elif var_h < 0.0:
                var_h = 0.0

        return var_h
        #print "hum : %6.2f %" % (var_h)

def setup():
        osrs_t = 1                      #Temperature oversampling x 1
        osrs_p = 1                      #Pressure oversampling x 1
        osrs_h = 1                      #Humidity oversampling x 1
        mode   = 3                      #Normal mode
        t_sb   = 5                      #Tstandby 1000ms
        filter = 0                      #Filter off
        spi3w_en = 0                    #3-wire SPI Disable

        ctrl_meas_reg = (osrs_t << 5) | (osrs_p << 2) | mode
        config_reg    = (t_sb << 5) | (filter << 2) | spi3w_en
        ctrl_hum_reg  = osrs_h

        writeReg(0xF2,ctrl_hum_reg)
        writeReg(0xF4,ctrl_meas_reg)
        writeReg(0xF5,config_reg)

setup()
get_calib_param()

if __name__ == '__main__':
        try:
                readData()
        except KeyboardInterrupt:
                pass

Golang版

参考URL Armoris日記 ラズパイと BME280 で温度・湿度・気圧を測って グラフ化して表示する

Raspberry Pi4B 32biOSの場合

$ uname -a
Linux ps2 5.10.63-v7l+ #1457 SMP Tue Sep 28 11:26:14 BST 2021 armv7l GNU/Linux

コンパイル時に次を設定

$ GOOS=linux && GOARCH=arm && GOARM=6

const recordIntervalSec = 180で、データを投げる周期を指定します。(自分の環境では $HOME/influx/配下に配置)

package main

import (
    "context"
    "fmt"
    "time"

    influxdb2 "github.com/influxdata/influxdb-client-go/v2"
    "github.com/influxdata/influxdb-client-go/v2/api"
    "github.com/quhar/bme280"
    "golang.org/x/exp/io/i2c"
)

func initBme280() (*bme280.BME280, error) {
    d, err := i2c.Open(&i2c.Devfs{Dev: "/dev/i2c-1"}, 0x76) // 0x76は今回使用す るBME280のI2Cアドレス
    if err != nil {
        return nil, err
    }

    b := bme280.New(d)
    err = b.Init()
    return b, err
}

func recordSensorValues(b *bme280.BME280, w api.WriteAPIBlocking, measurement string) (err error) {
    t, p, h, err := b.EnvData()
    if err != nil {
        return
    }
    point := influxdb2.NewPoint(
        measurement,
        map[string]string{},
        map[string]interface{}{
            "temperature": t,
            "humidity":    h,
            "pressure":    p,
        },
        time.Now(),
    )
    err = w.WritePoint(context.Background(), point)
    if err != nil {
        return
    }
    fmt.Printf("Temp: %.2fC, Hum: %.2f%%, Press: %.2fhpa\n", t, h, p)
    return
}

func main() {
    const influxURL = "http://xxx.xxx.xxx.xxx:8086"      // InfluxDB APIのURL
    const userName = "*****"                      // influxDBのUser名
    const password = "*****"                       // influxDBのUserのPassword
    const db = "iot01"                             // influxDBのDatabase
    const measurement = "sensors"                  // influxDBのMeasurement(存在しない場合は実行時に生成される)
    const recordIntervalSec = 180                  // 値を書き込む間隔(秒)

    b, err := initBme280()
    if err != nil {
        panic(err)
    }
    c := influxdb2.NewClient(influxURL, fmt.Sprintf("%s:%s", userName, password))
    w := c.WriteAPIBlocking("", db+"/autogen")

    ticker := time.NewTicker(recordIntervalSec * time.Second)
    defer ticker.Stop()
    for {
        select {
        case <-ticker.C:
            err = recordSensorValues(b, w, measurement)
            if err != nil {
                panic(err)
            }
        }
    }
}

RockPi4で64bit OSの場合

$ uname -a
Linux rock 4.4.154-110-rockchip-gcef30e88a9f5 #1 SMP Mon Jun 22 07:37:10 UTC 2020 aarch64 aarch64 aarch64 GNU/Linux

aptでインストールしたgolangのバージョンが古いので、新しいバージョンを取得

$ GOOS=linux && GOARCH=arm64 && GOARM=6 でコンパイルはできるが、実行時にエラーとなる。(chip IDが違うよ! というエラー)

mars@rock:~/influx$ ~/go/bin/go build iot-sensor-logger.go
go: downloading github.com/influxdata/influxdb-client-go/v2 v2.5.1
go: downloading golang.org/x/net v0.0.0-20210119194325-5f4716e94777
go: downloading github.com/deepmap/oapi-codegen v1.8.2
go: downloading github.com/pkg/errors v0.9.1
go: downloading gopkg.in/yaml.v2 v2.3.0
go: downloading github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839

$ ./iot-sensor-logger
panic: chip ID is different than expected, want(60), got(58)

goroutine 1 [running]:
main.main()
        /home/mars/influx/iot-sensor-logger.go:58 +0x1bc

自分の環境では、実行時にエラーとなってデータを取得できない。 GOARM=7も試してみたがNG。コンパイルで生成されたコードの情報は、問題なさそうなので、参照しているライブラリーに問題があるのか???

$ file iot-sensor-logger
iot-sensor-logger: ELF 64-bit LSB executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-aarch64.so.1, not stripped

ちなみに、【Golang】がサポートしている GOOS と GOARCH の一覧

$ go tool dist list
aix/ppc64
android/386
android/amd64
android/arm
android/arm64
darwin/amd64
darwin/arm64
dragonfly/amd64
freebsd/386
freebsd/amd64
freebsd/arm
freebsd/arm64
illumos/amd64
ios/amd64
ios/arm64
js/wasm
linux/386
linux/amd64
linux/arm
linux/arm64
linux/mips
linux/mips64
linux/mips64le
linux/mipsle
linux/ppc64
linux/ppc64le
linux/riscv64
linux/s390x
netbsd/386
netbsd/amd64
netbsd/arm
netbsd/arm64
openbsd/386
openbsd/amd64
openbsd/arm
openbsd/arm64
openbsd/mips64
plan9/386
plan9/amd64
plan9/arm
solaris/amd64
windows/386
windows/amd64
windows/arm
windows/arm64