首页 > 解决方案 > Raspberry Pi,套接字错误“socket.gaierror:[Errno -3] 名称解析暂时失败”

问题描述

我一直在为我的迷你水培设置开发一个简单的传感器记录器,使用 Google IOT Arm 示例代码(原始代码https://github.com/ARM-software/Cloud-IoT-Core-Kit-Examples/blob/master /CPUTemp/pi_cpu_temp_mqtt.py )。

它一直工作得很好。每 60 秒,有效负载将传感器数据传送到 Google Cloud,我在 Data Studio 中对其进行跟踪。

我添加了 Adafruit 传感器,并且已经愉快地跟踪传感器数据几个星期了。

问题

我的路由器每天凌晨 3 点自动重启一次,这导致我的 Python 代码挂起。我可以简单地停止重新启动,而是更愿意完全捕获或避免错误,以防将来出现更多不寻常的连接问题。

希望

能够重新连接并继续向 Google Cloud 发送数据的能力。

我将来会添加控制功能,例如继电器灯和风扇,因此可靠性对于帮助我的植物生长至关重要。

完整的错误

 Traceback (most recent call last):
  File "lily_telemetry.py", line 220, in <module>
    main()
  File "lily_telemetry.py", line 217, in main
    main()
  File "lily_telemetry.py", line 185, in main
    client.connect(args.mqtt_bridge_hostname, args.mqtt_bridge_port)
  File "/home/pi/.local/lib/python3.5/site-packages/paho/mqtt/client.py", line 839, in connect
    return self.reconnect()
  File "/home/pi/.local/lib/python3.5/site-packages/paho/mqtt/client.py", line 962, in reconnect
    sock = socket.create_connection((self._host, self._port), source_address=(self._bind_address, 0))
  File "/usr/lib/python3.5/socket.py", line 694, in create_connection
    for res in getaddrinfo(host, port, 0, SOCK_STREAM):
  File "/usr/lib/python3.5/socket.py", line 733, in getaddrinfo
    for res in _socket.getaddrinfo(host, port, family, type, proto, flags):
socket.gaierror: [Errno -3] Temporary failure in name resolution

编码

#!/usr/bin/python

import argparse
import json
import datetime
import time
import ssl
import subprocess

import board
import busio
import adafruit_mcp9808
import adafruit_tsl2591
import digitalio

import jwt
import paho.mqtt.client as mqtt

# Update and publish temperature readings at a rate of SENSOR_POLL per second.
SENSOR_POLL=60

# Initialise I2C Master Bus
i2c_bus = busio.I2C(board.SCL, board.SDA)

# Set temperature sensor inputs MCP9808 board
mcp1 = adafruit_mcp9808.MCP9808(i2c_bus)
mcp2 = adafruit_mcp9808.MCP9808(i2c_bus,0x1C)

# Set light sensor inputs TSL2591 board
tsl = adafruit_tsl2591.TSL2591(i2c_bus)

def create_jwt(project_id, private_key_file, algorithm):
  """Create a JWT (https://jwt.io) to establish an MQTT connection."""
  token = {
      'iat': datetime.datetime.utcnow(),
      'exp': datetime.datetime.utcnow() + datetime.timedelta(minutes=60),
      'aud': project_id
  }
  with open(private_key_file, 'r') as f:
    private_key = f.read()
  print('Creating JWT using {} from private key file {}' .format(algorithm, private_key_file))
  return jwt.encode(token, private_key, algorithm=algorithm)

def error_str(rc):
  """Convert a Paho error to a human readable string."""
  return '{}: {}'.format(rc, mqtt.error_string(rc))

class Device(object):
  """Represents the state of a single device."""
  def __init__(self):
    self.temperature = 0
    self.connected = False

  def update_sensor_data(self):
    # CPU Temp
    rawdata = subprocess.check_output(["sudo", "/opt/vc/bin/vcgencmd", "measure_temp"]).decode().split('=', 1)[-1].rstrip()
    sendtemp = '"temp":"{}"' .format(rawdata.replace("'", "-").split("-", 1)[0])
    self.temperature = sendtemp

    # Temperature Sensor 1 Data
    self.sensortemperature_1 = '"sensor_temp_1":"{}"' .format(mcp1.temperature)

    # Temperature Sensor 2 Data
    self.sensortemperature_2 = '"sensor_temp_2":"{}"' .format(mcp2.temperature)

    # Light Sensor Data
    lux = tsl.lux
    infrared = tsl.infrared
    visible = tsl.visible
    full_spectrum = tsl.full_spectrum
    self.lightsensor_lux = '"light_sensor_lux":"{}"' .format(round(lux,4))
    self.lightsensor_infrared = '"light_sensor_infrared":"{}"' .format(infrared)
    self.lightsensor_visible = '"light_sensor_visible":"{}"' .format(visible)
    self.lightsensor_full_spectrum = '"light_sensor_full_spectrum":"{}"' .format(full_spectrum)

  def wait_for_connection(self, timeout):
    """Wait for the device to become connected."""
    total_time = 0
    while not self.connected and total_time < timeout:
      time.sleep(1)
      total_time += 1

    if not self.connected:
      raise RuntimeError('Could not connect to MQTT bridge.')

  def on_connect(self, unused_client, unused_userdata, unused_flags, rc):
    """Callback for when a device connects."""
    print('Connection Result:', error_str(rc))
    self.connected = True

  def on_disconnect(self, unused_client, unused_userdata, rc):
    """Callback for when a device disconnects."""
    print('Disconnected:', error_str(rc))
    print('...')
    main()
    self.connected = False

  def on_publish(self, unused_client, unused_userdata, unused_mid):
    """Callback when the device receives a PUBACK from the MQTT bridge."""
    print('Published message.')
    print("Waiting {} seconds." .format(SENSOR_POLL))

  def on_subscribe(self, unused_client, unused_userdata, unused_mid,
                   granted_qos):
    """Callback when the device receives a SUBACK from the MQTT bridge."""
    print('Subscribed: ', granted_qos)
    if granted_qos[0] == 128:
      print('Subscription failed.')

def parse_command_line_args():
  """Parse command line arguments."""
  parser = argparse.ArgumentParser(
      description='Lily Grow Logging using MQTT')
  parser.add_argument(
      '--project_id', required=True, help='GCP cloud project name')
  parser.add_argument(
      '--registry_id', required=True, help='Cloud IoT registry id')
  parser.add_argument('--device_id', required=True, help='Cloud IoT device id')
  parser.add_argument(
      '--private_key_file', required=True, help='Path to private key file.')
  parser.add_argument(
      '--algorithm',
      choices=('RS256', 'ES256'),
      required=True,
      help='Which encryption algorithm to use to generate the JWT.')
  parser.add_argument(
      '--cloud_region', default='us-central1', help='GCP cloud region')
  parser.add_argument(
      '--ca_certs',
      default='roots.pem',
      help='CA root certificate. Get from https://pki.google.com/roots.pem')
  parser.add_argument(
      '--num_messages',
      type=int,
      default=60,
      help='Number of messages to publish.')
  parser.add_argument(
      '--mqtt_bridge_hostname',
      default='mqtt.googleapis.com',
      help='MQTT bridge hostname.')
  parser.add_argument(
      '--mqtt_bridge_port', default=8883, help='MQTT bridge port.')

  return parser.parse_args()

def main():
  args = parse_command_line_args()

  # Create our MQTT client and connect to Cloud IoT.
  client = mqtt.Client(
      client_id='projects/{}/locations/{}/registries/{}/devices/{}'.format(
          args.project_id, args.cloud_region, args.registry_id, args.device_id))
  client.username_pw_set(
      username='unused',
      password=create_jwt(args.project_id, args.private_key_file,
                          args.algorithm))
  client.tls_set(ca_certs=args.ca_certs, tls_version=ssl.PROTOCOL_TLSv1_2)

  device = Device()

  client.on_connect = device.on_connect
  client.on_publish = device.on_publish
  client.on_disconnect = device.on_disconnect
  client.on_subscribe = device.on_subscribe

  client.connect(args.mqtt_bridge_hostname, args.mqtt_bridge_port)

  client.loop_start()

  # This is the topic that the device will publish telemetry events (temperature
  # data) to.
  mqtt_telemetry_topic = '/devices/{}/events'.format(args.device_id)

  # Wait up to 5 seconds for the device to connect.
  device.wait_for_connection(5)

  # Update and publish temperature readings at a rate of SENSOR_POLL per second.
  for _ in range(args.num_messages):
    device.update_sensor_data()

  # Time
    sendtime = '"datetime":"{}"'.format(datetime.datetime.now().strftime("%Y-%m-%d"" ""%H:%M:%S.%f"))  
  # Payload
    payload = '{{''{},{},{},{},{},{},{},{}''}}'.format(device.temperature, device.sensortemperature_1,device.sensortemperature_2,device.lightsensor_lux,device.lightsensor_infrared,device.lightsensor_visible,device.lightsensor_full_spectrum,sendtime)
  # Message
    print('Payload: {}'.format(payload))
  # Publish, Sleep
    client.publish(mqtt_telemetry_topic, payload, qos=1)
    time.sleep(SENSOR_POLL)

  client.disconnect()
  client.loop_stop()
  print('Finished loop successfully. Goodbye!')
  main()

if __name__ == '__main__':
  main()

标签: python-3.xmqttraspberry-pi3

解决方案


感谢您 hardillb 的建议,使用from socket import gaierror

  try:
        client.connect(args.mqtt_bridge_hostname, args.mqtt_bridge_port)
  except gaierror as e:
        print('Gaierror {}.'.format(e))
        print('Waiting {} seconds before restarting Main'.format(sensor_delay))
        time.sleep(sensor_delay)
        restartlily()

restartlily()从顶部重新启动整个脚本


推荐阅读