Я строю беспроводную связь в реальном времени между базовой станцией ПК и дюжиной роботов, каждый из которых передает с частотой 60 Гц.
Для тестирования я написал скрипт Python, который отправляет многоадресные UDP-пакеты по моей сети WiFi,И затем в другой многоадресной группе у меня есть два ESP8266, отправляющих ответы.Один посылает сигнал с частотой 60 Гц, чтобы имитировать реальную настройку, а другой - в 10 раз быстрее, чтобы имитировать перегрузку с других устройств.
Сначала я посмотрел только на потерю пакетов, что было довольно ожидаемым.Но потом я начал смотреть в оба конца.Я заставил свой скрипт Python записать текущее время, и «реальный» узел вернул их обратно.(сообщения о спамере игнорируются)
При отключенном узле спаммера я получаю круговую передачу в 10 мс, но при спамере он начинается со 100 мс и медленно увеличивается до секунды.
Как я могу отладить эту проблему?
Я пытался использовать Wireshark, но, поскольку они представляют собой отдельные потоки UDP с пользовательскими данными в них, я не смог разобраться в каком-либо разумном анализе.
Я подумал, что пакеты, вероятно, где-то застряли в очереди, поэтому некоторые поиски в Google предложили посмотреть длину очереди с помощью следующей команды.Конечно, очередь не пуста, но если я отключу спамерский узел, длина очереди не изменится, а обратная передача сократится до 10 мс.
netstat -c --udp -an
Proto Recv-Q Send-Q Local Address Foreign Address State
udp 40256 0 0.0.0.0:8080 0.0.0.0:*
Я подумал, что мой код Pythonслишком медленно обрабатывать 600 сообщений в секунду.Работа на PyPy не имела абсолютно никакого значения, поэтому я немного колеблюсь, чтобы написать все это на C. Он имеет почти 2 мс на пакет, чтобы почти ничего не делать.
Код базовой станции:
import socket
import struct
import sched, time
import threading
base_multicast = '239.255.0.34'
robot_multicast = '239.255.0.35'
port = 8080
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('', port))
mreq = struct.pack("4sl", socket.inet_aton(robot_multicast), socket.INADDR_ANY)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
# schedule base station packets
send_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
send_sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2)
rate = 1/60
#sock.settimeout(rate)
s = sched.scheduler(time.time, time.sleep)
def send(now):
nxt = now+rate
s.enterabs(nxt, 1, send, argument=(nxt,))
tstr = struct.pack("d", time.time())
send_sock.sendto(tstr, (base_multicast, port))
print(".", end='')
s.enter(1, 1, send, argument=(time.time(),))
t = threading.Thread(target=s.run)
t.daemon = True
t.start()
start = time.time()
avg_dur = 0
avg_ping = 0
while True:
try:
data = sock.recv(10240)
except socket.timeout:
continue
now = time.time()
duration = now-start
start = now
avg_dur = 0.99*avg_dur + 0.01*duration
try:
(t,) = struct.unpack("d", data)
ping = now-t
avg_ping = 0.99*avg_ping + 0.01*ping
print(len(data), 1/avg_dur, avg_ping)
except struct.error: # spammer message
pass
Узел робота: (спаммер идентичен, за исключением того, что он отправляет replyPacket
10 раз в цикле)
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
const char* ssid = "mywifi";
const char* password = "password";
IPAddress base_multicast(239, 255, 0, 34);
IPAddress robot_multicast(239, 255, 0, 35);
WiFiUDP Udp;
unsigned int port = 8080; // local port to listen on
char incomingPacket[255]; // buffer for incoming packets
char replyPacket[] = "Hi there! Got the message :-))))"; // a reply string to send back
int rate = 1000/60;
unsigned long mytime;
unsigned long count;
float avg_diff = 0;
void setup()
{
Serial.begin(115200);
Serial.println();
Serial.printf("Connecting to %s ", ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
}
Serial.println(" connected");
//Udp.begin(localUdpPort);
Udp.beginMulticast(WiFi.localIP(), base_multicast, port);
Serial.printf("Now listening at IP %s, UDP port %d\n", WiFi.localIP().toString().c_str(), port);
mytime = millis();
}
void loop()
{
int packetSize = Udp.parsePacket();
if (packetSize)
{
// receive incoming UDP packets
unsigned long diff = micros() - mytime;
avg_diff = 0.9*avg_diff + 0.1*diff;
mytime = micros();
int rcv_rate = 1000000/avg_diff;
Serial.println(rcv_rate);
int len = Udp.read(incomingPacket, 255);
delay(random(rate));
//Serial.printf("UDP packet contents: %s\n", incomingPacket);
Udp.beginPacketMulticast(robot_multicast, port, WiFi.localIP());
Udp.write(incomingPacket, len);
Udp.endPacket();
}
}