-
Notifications
You must be signed in to change notification settings - Fork 10
Secondary Payload Telemetry Notes
These notes are to help set up a multiple-payload Wenet system, where a 'primary' Wenet payload multiplexed packets from 'secondary' payloads into the main RF downlink.
I used this guide to setup a Pi Zero W to be a Wifi AP: https://github.com/SurferTim/documentation/blob/6bc583965254fa292a470990c40b145f553f6b34/configuration/wireless/access-point.md
A few notes to remind myself what I had to change to set up the SHSSP Wifi-enabled master payload. These changes are in relation to the WiFi AP setup guide linked above.
Added:
dtoverlay=pi3-disable-bt
dtparam=watchdog=on
<at bottom of file>
interface wlan0
static ip_address=192.168.1.1/24
interface=wlan0
driver=nl80211
ssid=SHSSP-SkyFi
hw_mode=g
channel=3
wmm_enabled=0
macaddr_acl=0
auth_algs=1
ignore_broadcast_ssid=0
wpa=2
wpa_passphrase=<passphrase here>
wpa_key_mgmt=WPA-PSK
wpa_pairwise=TKIP
rsn_pairwise=CCMP
NOTE: This script was replaced by check_temp.py below.
This file was run on a 10-minute cron job, to check if the secondary payload was still connected, and if not, reboot the WiFi AP. z19a.local
was the hostname of the secondary payload.
ping -c4 z19a.local > /dev/null
if [ $? != 0 ]
then
echo $(date) >> /home/pi/apcheck.txt
echo "restart AP" >> /home/pi/apcheck.txt
sudo service hostapd stop
sleep 2
sudo ifconfig wlan0 down
sleep 5
sudo ifconfig wlan0 up
sleep 2
sudo service hostapd restart
fi
This script was started on boot via rc.local. It checked for the presence of the secondary payload on the wifi network, and if not present, restarted the wifi. It would also disable the wifi if the RPi CPU temperature reached a user-defined threshold.
#!/usr/bin/env python
#
# Example generation of some basic 'Secondary Payload' packets.
# This is a method by which another process or payload can inject data into
# the Wenet downlink telemetry channel.
#
# Refer to the equivalent sec_payload_rx_example in wenet/rx/
# for how to receive these packets on the ground-station end.
#
# Copyright (C) 2018 Mark Jessop <[email protected]>
# Released under GNU GPL v3 or later
#
import socket, struct, time, json, random, subprocess
def emit_secondary_packet(id=0, packet="", repeats = 1, hostname='<broadcast>', port=55674):
""" Send a Secondary Payload data packet into the network, to (hopefully) be
transmitted by a Wenet transmitter.
Keyword Arguments:
id (int): Payload ID number, 0 - 255
packet (): Packet data, packed as a byte array. Maximum of 254 bytes in size.
repeats (int): Number of times to re-transmit this packet. Defaults to 1.
hostname (str): Hostname of the Wenet transmitter. Defaults to using UDP broadcast.
port (int): UDP port of the Wenet transmitter. Defaults to 55674.
"""
telemetry_socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
# Set up the telemetry socket so it can be re-used.
telemetry_socket.setsockopt(socket.SOL_SOCKET,socket.SO_BROADCAST,1)
telemetry_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# We need the following if running on OSX.
try:
telemetry_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
except:
pass
# Place data into dictionary.
data = {'type': 'WENET_TX_SEC_PAYLOAD', 'id': int(id), 'repeats': int(repeats), 'packet': list(bytearray(packet))}
# Send to target hostname. If this fails just send to localhost.
try:
telemetry_socket.sendto(json.dumps(data), (hostname, port))
except socket.error:
telemetry_socket.sendto(json.dumps(data), ('127.0.0.1', port))
telemetry_socket.close()
# Global text message counter.
text_message_counter = 0
def create_text_message(message):
""" Create a text message packet, for transmission within a 'secondary payload' message.
This is in the same format as a standard wenet text message, however the maximum message
length is shortened by 2 bytes to 250 bytes, due to the extra header overhead.
Keyword Arguments:
message(str): A text message as a string, up to 250 characters in length.
"""
global text_message_counter
text_message_counter = (text_message_counter+1)%65536
# Clip message if required.
if len(message) > 250:
message = message[:250]
# We will use the Wenet standard text message format, which has a packet type of 0x00,
# and consists of a length field, a message count, and then the message itself.
_PACKET_TYPE = 0x00
_PACKET_LEN = len(message)
_PACKET_COUNTER = text_message_counter
# Assemble the packet.
_packet = struct.pack(">BBH", _PACKET_TYPE, _PACKET_LEN, _PACKET_COUNTER) + message
return _packet
def create_arbitrary_float_packet(data=[0.0, 0.1]):
""" Create a payload that contains a list of floating point numbers.
Keyword Arguments:
data (list): A list of floating point numbers to package and send.
"""
# Clip the amount of numbers to send to a maximum of 63 (we only have 252 bytes to fit data into)
if len(data) > 63:
data = data[:63]
# Our packet format will consist of a packet type, a length field, and then the data.
_PACKET_TYPE = 0x10 # We will define a packet type of 0x10 to be a list of floats.
_PACKET_LEN = len(data)
# Convert the list of floats into a byte array representation.
_float_bytes = bytes("")
for _val in data:
_float_bytes += struct.pack(">f", _val)
# Now assemble the final packet.
_packet = struct.pack(">BB", _PACKET_TYPE, _PACKET_LEN) + _float_bytes
return _packet
def get_temperature():
try:
data = subprocess.check_output("/opt/vc/bin/vcgencmd measure_temp", shell=True)
temp = data.split('=')[1].split('\'')[0]
return float(temp)
except Exception as e:
return -1
def send_message(message):
_txt_packet = create_text_message(message)
emit_secondary_packet(id=99, packet=_txt_packet, repeats=2)
print("Sent message:" + message)
def disable_ap():
# Otherwise, we shutdown the AP.
try:
subprocess.check_call("sudo service hostapd stop", shell=True)
time.sleep(1)
subprocess.check_call("sudo ifconfig wlan0 down", shell=True)
_last_action = time.time()
except Exception as e:
send_message("ERROR:" + str(e))
return False
return True
def enable_ap():
# Otherwise, we shutdown the AP.
try:
subprocess.check_call("sudo ifconfig wlan0 up", shell=True)
time.sleep(3)
subprocess.check_call("sudo service hostapd start", shell=True)
_last_action = time.time()
except Exception as e:
send_message("ERROR:" + str(e))
return False
return True
def bounce_ap():
# Otherwise, we shutdown the AP.
try:
subprocess.check_call("sudo service hostapd stop", shell=True)
time.sleep(1)
subprocess.check_call("sudo ifconfig wlan0 down", shell=True)
time.sleep(5)
subprocess.check_call("sudo ifconfig wlan0 up", shell=True)
time.sleep(3)
subprocess.check_call("sudo service hostapd start", shell=True)
_last_action = time.time()
except Exception as e:
send_message("ERROR:" + str(e))
return False
return True
def ping_test(ip_addr):
try:
result = subprocess.check_call("ping -c2 %s" % ip_addr, shell=True)
return True
except Exception as e:
#send_message("Could not contact Secondary Pi!")
return False
if __name__ == "__main__":
# Define ourselves to be 'sub-payload' number 3.
PAYLOAD_ID = 99
DISABLE_TEMP_LIMIT = 80.0
ENABLE_TEMP_LIMIT = 78.0
AP_INHIBIT = False
SISTER_PAYLOAD = "z19a.local"
SISTER_LAST_HEARD = 0
SISTER_TIMEOUT = 6
try:
while True:
# Get the system temperature
_temp = get_temperature()
_message = "CPU Temperature: %.1f degC" % _temp
print(_message)
_txt_packet = create_text_message(_message)
emit_secondary_packet(id=PAYLOAD_ID, packet=_txt_packet, repeats=2)
if _temp > DISABLE_TEMP_LIMIT:
# Overtemp - disable AP, if it's enabled
if AP_INHIBIT == False:
_success = disable_ap()
AP_INHIBIT = True
send_message("DISABLED WIFI AP DUE TO OVERHEATING.")
elif _temp < ENABLE_TEMP_LIMIT:
# Undertemp - re-enable AP, if it is disabled
if AP_INHIBIT:
_success = enable_ap()
AP_INHIBIT = False
send_message("ENABLED WIFI AP")
else:
pass
_sister_ok = ping_test(SISTER_PAYLOAD)
if _sister_ok:
SISTER_LAST_HEARD = 0
else:
SISTER_LAST_HEARD += 1
send_message("Could not contact sister payload (%d/%d)" % (SISTER_LAST_HEARD,SISTER_TIMEOUT))
if SISTER_LAST_HEARD > SISTER_TIMEOUT:
if AP_INHIBIT == False:
send_message("No contact from sister payload. Restarted AP.")
bounce_ap()
SISTER_LAST_HEARD = 0
time.sleep(30)
# Keep going unless we get a Ctrl + C event
except KeyboardInterrupt:
print("Closing")