Interpreting keypresses sent to raspberry-pi through uv4l-webrtc datachannel - raspberry-pi

I apologize if this doesn't make sense since I'm still a newbie with using a raspberry pi and this is my first time posting on StackOverflow.
I am making a web app that lets me stream video to and from a raspberry pi while also letting me send keycodes. The sent keycodes would ultimately let me control servos on a drone. After scouring the internet, I figured that the simplest way to stream 2-way video is by using uv4l so I have it installed along with uv4l-webrtc on my raspberry pi. I hooked up some GPIO pins to a flight controller and I am using pigpio to send PWM signals to it, which I then monitor using CleanFlight.
Right now, I can manipulate with keypresses the roll, pitch, etc. of the flight controller using a python script if I access the pi remotely using VNC, but I would like to ultimately be able to do this through my custom web page that is being served by the uv4l-server. I am trying to use the WebRTC Data Channels, but I'm having some trouble understanding what I would need to do to recognize the messages sent through the data channels. I know that the data channels are opened when a video call is initiated and I've tried the test in this link to see if I can indeed send keycodes to the pi (and I can).
My problem right now is that I have no idea where those sent messages go or how I can get them so I can incorporate them into my python script. Would I need to make a server that would listen for the keycodes being sent to the pi?
tl;dr I have a python script on a raspberry pi to control servos on a flight controller using keypresses and a separate webpage that streams video using WebRTC, but I have no idea how to combine them together using WebRTC data channels.
Thanks to #adminkiam for the solution. Here's a version of the python script that now listens to the socket. It's essentially a variation of this code by the person who made pigpio:
import socket
import time
import pigpio
socket_path = '/tmp/uv4l.socket'
try:
os.unlink(socket_path)
except OSError:
if os.path.exists(socket_path):
raise
s = socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET)
ROLL_PIN = 13
PITCH_PIN = 14
YAW_PIN = 15
MIN_PW = 1000
MID_PW = 1500
MAX_PW = 2000
NONE = 0
LEFT_ARROW = 1
RIGHT_ARROW = 2
UP_ARROW = 3
DOWN_ARROW = 4
LESS_BTN = 5
GREATER_BTN = 6
print 'socket_path: %s' % socket_path
s.bind(socket_path)
s.listen(1)
def getch(keyCode):
key = NONE
if keyCode == 188:
key = LESS_BTN
elif keyCode == 190:
key = GREATER_BTN
elif keyCode == 37:
key = LEFT_ARROW
elif keyCode == 39:
key = RIGHT_ARROW
elif keyCode == 38:
key = UP_ARROW
elif keyCode == 40:
key = DOWN_ARROW
return key
def cleanup():
pi.set_servo_pulsewidth(ROLL_PIN, 0)
pi.set_servo_pulsewidth(PITCH_PIN, 0)
pi.set_servo_pulsewidth(YAW_PIN, 0)
pi.stop()
while True:
print 'awaiting connection...'
connection, client_address = s.accept()
print 'client_address %s' % client_address
try:
print 'established connection with', client_address
pi = pigpio.pi()
rollPulsewidth = MID_PW
pitchPulsewidth = MID_PW
yawPulsewidth = MID_PW
pi.set_servo_pulsewidth(ROLL_PIN, rollPulsewidth)
pi.set_servo_pulsewidth(PITCH_PIN, pitchPulsewidth)
pi.set_servo_pulsewidth(YAW_PIN, yawPulsewidth)
while True:
data = connection.recv(16)
print 'received message"%s"' % data
time.sleep(0.01)
key = getch(int(data))
rollPW = rollPulsewidth
pitchPW = pitchPulsewidth
yawPW = yawPulsewidth
if key == UP_ARROW:
pitchPW = pitchPW + 10
if pitchPW > MAX_PW:
pitchPW = MAX_PW
elif key == DOWN_ARROW:
pitchPW = pitchPW - 10
if pitchPW < MIN_PW:
pitchPW = MIN_PW
elif key == LEFT_ARROW:
rollPW = rollPW - 10
if rollPW < MIN_PW:
rollPW = MIN_PW
elif key == RIGHT_ARROW:
rollPW = rollPW + 10
if rollPW > MAX_PW:
rollPW = MAX_PW
elif key == GREATER_BTN:
yawPW = yawPW + 10
if yawPW > MAX_PW:
yawPW = MAX_PW
elif key == LESS_BTN:
yawPW = yawPW - 10
if yawPW < MIN_PW:
yawPW = MIN_PW
if rollPW != rollPulsewidth:
rollPulsewidth = rollPW
pi.set_servo_pulsewidth(ROLL_PIN, rollPulsewidth)
if pitchPW != pitchPulsewidth:
pitchPulsewidth = pitchPW
pi.set_servo_pulsewidth(PITCH_PIN, pitchPulsewidth)
if yawPW != yawPulsewidth:
yawPulsewidth = yawPW
pi.set_servo_pulsewidth(YAW_PIN, yawPulsewidth)
if data:
print 'echo data to client'
connection.sendall(data)
else:
print 'no more data from', client_address
break
finally:
# Clean up the connection
cleanup()
connection.close()

When a WebRTC data channel is created between UV4L and the other WebRTC peer (i.e. a browser, Janus Gateway, etc...), UV4L creates a full-duplex Unix Domain Socket (/tmp/uv4l.socket by default) from/to which you can receive/send messages on the Raspberry Pi. Your python script should just open, listen and read to the socket for the incoming messages from the e.g. web application and/or write the messages to the same socket for the web app to receive them. An example doing this in C++ is under the link to the tutorial you pointed out in your question:
/*
Copyright (c) 2016 info#linux-projects.org
All rights reserved.
Redistribution and use in source and binary forms are permitted
provided that the above copyright notice and this paragraph are
duplicated in all such forms and that any documentation,
advertising materials, and other materials related to such
distribution and use acknowledge that the software was developed
by the linux-projects.org. The name of the
linux-projects.org may not be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
/*
* This is a simple echo server.
* It creates to a unix domain socket of type SOCK_SEQPACKET specified by
* command line, listens to it waiting for incoming messages from clients
* (e.g. UV4L) and replies the received messages back to the senders.
*
* Example:
* $ ./datachannel_server /tmp/uv4l.socket
*
* To compile this program you need boost v1.60 or greater, for example:
* g++ -Wall -I/path/to/boost/include/ -std=c++11 datachannel_server.cpp -L/path/to/boost/lib -l:libboost_coroutine.a -l:libboost_context.a -l:libboost_system.a -l:libboost_thread.a -pthread -o datachannel_server
*/
#include <boost/asio/io_service.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/asio/write.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/asio.hpp>
#include <memory>
#include <cstdio>
#include <array>
#include <functional>
#include <iostream>
#if !defined(BOOST_ASIO_HAS_LOCAL_SOCKETS)
#error Local sockets not available on this platform.
#endif
constexpr std::size_t MAX_PACKET_SIZE = 1024 * 16;
namespace seqpacket {
struct seqpacket_protocol {
int type() const {
return SOCK_SEQPACKET;
}
int protocol() const {
return 0;
}
int family() const {
return AF_UNIX;
}
using endpoint = boost::asio::local::basic_endpoint<seqpacket_protocol>;
using socket = boost::asio::generic::seq_packet_protocol::socket;
using acceptor = boost::asio::basic_socket_acceptor<seqpacket_protocol>;
#if !defined(BOOST_ASIO_NO_IOSTREAM)
/// The UNIX domain iostream type.
using iostream = boost::asio::basic_socket_iostream<seqpacket_protocol>;
#endif
};
}
using seqpacket::seqpacket_protocol;
struct session : public std::enable_shared_from_this<session> {
explicit session(seqpacket_protocol::socket socket) : socket_(std::move(socket)) {}
~session() {
//std::cerr << "session closed\n";
}
void echo(boost::asio::yield_context yield) {
auto self = shared_from_this();
try {
for (;;) {
seqpacket_protocol::socket::message_flags in_flags = MSG_WAITALL, out_flags = MSG_WAITALL;
// Wait for the message from the client
auto bytes_transferred = socket_.async_receive(boost::asio::buffer(data_), in_flags, yield);
// Write the same message back to the client
socket_.async_send(boost::asio::buffer(data_, bytes_transferred), out_flags, yield);
}
} catch (const std::exception& e) {
std::cerr << e.what() << '\n';
socket_.close();
}
}
void go() {
boost::asio::spawn(socket_.get_io_service(), std::bind(&session::echo, this, std::placeholders::_1));
}
private:
seqpacket_protocol::socket socket_;
std::array<char, MAX_PACKET_SIZE> data_;
};
int main(int argc, char* argv[]) {
try {
if (argc != 2) {
std::cerr << "Usage: datachannel_server <file> (e.g. /tmp/uv4l.socket)\n";
std::cerr << "*** WARNING: existing file is removed ***\n";
return EXIT_FAILURE;
}
boost::asio::io_service io_service;
std::remove(argv[1]);
boost::asio::spawn(io_service, [&](boost::asio::yield_context yield) {
seqpacket_protocol::acceptor acceptor_(io_service, seqpacket_protocol::endpoint(argv[1]));
for (;;) {
boost::system::error_code ec;
seqpacket_protocol::socket socket_(io_service);
acceptor_.async_accept(socket_, yield[ec]);
if (!ec)
std::make_shared<session>(std::move(socket_))->go();
}
});
io_service.run();
} catch (std::exception& e) {
std::cerr << "Exception: " << e.what() << "\n";
return EXIT_FAILURE;
}
}

Related

How I do an ESP8266 HTTPupdate via private Github repository?

I try to do an update firmeware via Git repo from an ESP8266. But I don't know how. The repo is private, that mean I need a password, I read that I can use HTTPclient library for authentication. How Github's authentication works?
Also, do I need some extra code for Update library? HTTPclient supports HTTPS?
EDIT: Here some example of my code, but is for a public repo:
update.cpp (I have it in a separate header file)
//#define repo "https://github.com/username/reponame/branch/path/to/file?raw=true"
#define repo "https://raw.githubusercontent.com/username/reponame/branch/path/to/file"
t_httpUpdate_return ret = ESPhttpUpdate.update(client, repo);
// Or:
//t_httpUpdate_return ret = ESPhttpUpdate.update(client, "server", 80, "file.bin");
I have configured httpUpdate error message, it show the next error:
CALLBACK: HTTP update fatal error code -5
HTTP_UPDATE_FAILD Error (-5): HTTP error: connection lost
There is a different way to do an update from GitHub, first GitHub use a HTTPS connection, that mean you need configure before a TLS/SSL setting, Also, port 80 is for insecure connections, 443 is designed for secure connections.
Public repository (insecure)
This is the easier way, just add .setInsecure() to the wifi client from WiFiClientSecure.h library, this allow you to stablish a connection ignoring all alerts from http connection.
WiFiClientSecure client;
client.setInsecure();
Is insecure, only do this for testing, not for production.
You must use https://raw.githubusercontent.com, this is for download raw data from GitHub's public repos, just the file. Your full link to the file must be:
#define repo "https://raw.githubusercontent.com/<user>/<repo>/master/<path to the .bin>"
t_httpUpdate_return ret = ESPhttpUpdate.update(client, repo);
Replace <user> with your user name, and <repo> with your repository's name. <path to the .bin> is something like "folder/folder/firmware.bin"
Public repository (secure):
There is an example in the official GitHub repository of ESP8266/Arduino.
https://github.com/esp8266/Arduino/blob/master/libraries/ESP8266httpUpdate/examples/httpUpdateSecure/httpUpdateSecure.ino
You can follow this example for a secure connection with httpUpdate. Also you will need to download the certs, this can do by executing the next script in the same folder of your project:
https://github.com/esp8266/Arduino/blob/master/libraries/ESP8266WiFi/examples/BearSSL_CertStore/certs-from-mozilla.py
If you are using windows, to run this script, you will need add "OpenSSL.exe" to the path, Git comes with it, you add the Git's bin folder to the path. Also, you will need one more file "ar.exe", this come with the ESP8266 core. You can also put this two .exe files in the same folder of the script.
For Arduino IDE is something like:
%userprofile%\AppData\Local\Arduino15\packages\esp8266\tools\xtensa-lx106-elf-gcc\2.5.0-4-b40a506\xtensa-lx106-elf\bin
For PlaformIO is:
%userprofile%\.platformio\packages\toolchain-xtensa\xtensa-lx106-elf\bin
When the script finish, this will create a folder named data with certs.ar inside. Upload this file system image to ESP8266 with LittleFS.
Private repository:
This is the same as the previous one, just couple of things to change, and we will make change to the ESP8266httpUpdate library. We use the same example to httpupdatesecure and you will need to configure a token in your GitHub account.
Follow the instructions of the help page of GitHub for create a token:
https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line
You only need this option selected
Copy the token and save it, you only able see one time.
You can't use raw.githubusercontent.com, will give you error 404, this only work for public repos. You need: api.github.com. You full link looks like:
https://api.github.com/repos/<user>/<repo>/contents/<path to the .bin>
And you need to add the headers to the http request, in the ESP8266httpUpdate.cpp, you must put it in the function HTTPUpdateResult ESP8266HTTPUpdate::handleUpdate in the part where start add headers:
http.addHeader(F("Accept"), "application/vnd.github.v3.raw");
http.addHeader(F("authorization"), "Bearer <your token>");
Replace <your token> with the one that you created and saved before.
Remember, edit this library will affect all your future projects, so, when you are done revert or comment the two headers that you added to the library.
NOTE: Using a token will allow your application access to all your
repos, even if your token is read-only. Currently, is not possible generate a token for a specific repo on GitHub. Be careful if you share your
code. Better use a dummy account with only one repo for this.
[Edit] - It's wokring now, it was an arduino core bug, installed 2.7.4 and now it works(come form 3.0.2-dev)
Doesn't work here "Verify Bin Header Failed", tryied hosting bin on github, 000webhosting, surge, not seens anything related to web server problem :(
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <ESP8266httpUpdate.h>
//#include <WiFiClientSecure.h>
#include <CertStoreBearSSL.h>
BearSSL::CertStore certStore;
#include <time.h>
#include <FS.h>
#include <LittleFS.h>
const String FirmwareVer={"1"};
//#define URL_fw_Version "/teste/key/version.txt"
//#define URL_fw_Bin "https://pacauiot.surge.sh/teste/key/firmware.bin"
//const char* host = "pacauiot.surge.sh";
#define URL_fw_Bin "https://fourieristic-prison.000webhostapp.com/meucu.php"
const char* host = "fourieristic-prison.000webhostapp.com";
const int httpsPort = 443;
const char* ssid = "wifi";
const char* password = "wifipass";
#define RTC_UTC_TEST 1510592825
void setClock()
// *******************************************************************************************
{
// see https://github.com/esp8266/Arduino/issues/4637
time_t now;
now = time(nullptr); // if there's no time, this will have a value of 28800; Thu Jan 1 08:00:00 1970
Serial.print("Initial time:"); Serial.println(now);
Serial.println(ctime(&now));
int myTimezone = -7;
int dst = 0;
int SecondsPerHour = 3600;
int MAX_TIME_RETRY = 60;
int i = 0;
// it is unlikely that the time is already set since we have no battery;
// if no time is avalable, then try to set time from the network
if (now <= 1500000000) {
// try to set network time via ntp packets
configTime(0, 0, "pool.ntp.org", "time.nist.gov"); // see https://github.com/esp8266/Arduino/issues/4749#issuecomment-390822737
// Starting in 2007, most of the United States and Canada observe DST from
// the second Sunday in March to the first Sunday in November.
// example setting Pacific Time:
setenv("TZ", "EST4EDT", 1); // see https://users.pja.edu.pl/~jms/qnx/help/watcom/clibref/global_data.html
// | month 3, second sunday at 2:00AM
// | Month 11 - firsst Sunday, at 2:00am
// Mm.n.d
// The dth day(0 <= d <= 6) of week n of month m of the year(1 <= n <= 5, 1 <= m <= 12, where
// week 5 means "the last d day in month m", which may occur in the fourth or fifth week).
// Week 1 is the first week in which the dth day occurs.Day zero is Sunday.
tzset();
Serial.print("Waiting for time(nullptr).");
i = 0;
while (!time(nullptr)) {
Serial.print(".");
delay(1000);
i++;
if (i > MAX_TIME_RETRY) {
Serial.println("Gave up waiting for time(nullptr) to have a valid value.");
break;
}
}
}
Serial.println("");
// wait and determine if we have a valid time from the network.
now = time(nullptr);
i = 0;
Serial.print("Waiting for network time.");
while (now <= 1500000000) {
Serial.print(".");
delay(1000); // allow a few seconds to connect to network time.
i++;
now = time(nullptr);
if (i > MAX_TIME_RETRY) {
Serial.println("Gave up waiting for network time(nullptr) to have a valid value.");
break;
}
}
Serial.println("ok");
// get the time from the system
char *tzvalue;
tzvalue = getenv("TZ");
Serial.print("Network time:"); Serial.println(now);
Serial.println(ctime(&now));
Serial.print("tzvalue for timezone = "); Serial.println(tzvalue);
// TODO - implement a web service that returns current epoch time to use when NTP unavailable (insecure SSL due to cert date validation)
// some networks may not allow ntp protocol (e.g. guest networks) so we may need to fudge the time
if (now <= 1500000000) {
Serial.println("Unable to get network time. Setting to fixed value. \n");
// set to RTC text value
// see https://www.systutorials.com/docs/linux/man/2-settimeofday/
//
//struct timeval {
// time_t tv_sec; /* seconds */
// suseconds_t tv_usec; /* microseconds */
//};
timeval tv = { RTC_UTC_TEST, 0 };
//
//struct timezone {
// int tz_minuteswest; /* minutes west of Greenwich */
// int tz_dsttime; /* type of DST correction */
//};
timezone tz = { myTimezone * 60 , 0 };
// int settimeofday(const struct timeval *tv, const struct timezone *tz);
settimeofday(&tv, &tz);
}
now = time(nullptr);
Serial.println("Final time:"); Serial.println(now);
Serial.println(ctime(&now));
}
void FirmwareUpdate()
{
//WiFiClientSecure client;
BearSSL::WiFiClientSecure client;
bool mfln = client.probeMaxFragmentLength(host, 443, 1024); // server must be the same as in ESPhttpUpdate.update()
Serial.printf("MFLN supported: %s\n", mfln ? "yes" : "no");
if (mfln) {
client.setBufferSizes(1024, 1024);
}
client.setCertStore(&certStore);
//client.setTrustAnchors(&cert);
// if (!client.connect(host, httpsPort)) {
// Serial.println("Connection failed");
// return;
// }
// client.print(String("GET ") + URL_fw_Version + " HTTP/1.1\r\n" +
// "Host: " + host + "\r\n" +
// "User-Agent: BuildFailureDetectorESP8266\r\n" +
// "Connection: close\r\n\r\n");
// while (client.connected()) {
// String line = client.readStringUntil('\n');
// if (line == "\r") {
// //Serial.println("Headers received");
// break;
// }
// }
// String payload = client.readStringUntil('\n');
//
// payload.trim();
// if(payload.equals(FirmwareVer) )
// {
// Serial.println("Device already on latest firmware version");
// }
if(1==2){
}
else
{
Serial.println("New firmware detected");
ESPhttpUpdate.setLedPin(LED_BUILTIN, LOW);
t_httpUpdate_return ret = ESPhttpUpdate.update(client, URL_fw_Bin);
switch (ret) {
case HTTP_UPDATE_FAILED:
Serial.printf("HTTP_UPDATE_FAILD Error (%d): %s\n", ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str());
break;
case HTTP_UPDATE_NO_UPDATES:
Serial.println("HTTP_UPDATE_NO_UPDATES");
break;
case HTTP_UPDATE_OK:
Serial.println("HTTP_UPDATE_OK");
break;
}
}
}
void connect_wifi();
unsigned long previousMillis_2 = 0;
unsigned long previousMillis = 0; // will store last time LED was updated
const long interval = 10000;
const long mini_interval = 5000;
void repeatedCall(){
unsigned long currentMillis = millis();
if ((currentMillis - previousMillis) >= interval)
{
// save the last time you blinked the LED
previousMillis = currentMillis;
setClock();
FirmwareUpdate();
}
if ((currentMillis - previousMillis_2) >= mini_interval) {
static int idle_counter=0;
previousMillis_2 = currentMillis;
Serial.print(" Active fw version:");
Serial.println(FirmwareVer);
Serial.print("Idle Loop(5s)...");
//Serial.println(idle_counter++);
if(idle_counter%2==0)
digitalWrite(LED_BUILTIN, HIGH);
else
digitalWrite(LED_BUILTIN, LOW);
if(WiFi.status() == !WL_CONNECTED)
connect_wifi();
}
}
void setup()
{
Serial.begin(115200);
Serial.println();
Serial.println();
Serial.println();
for (uint8_t t = 4; t > 0; t--) {
Serial.printf("[SETUP] WAIT %d...\n", t);
Serial.flush();
delay(1000);
}
Serial.println("Start Xuxu");
WiFi.mode(WIFI_STA);
connect_wifi();
setClock();
pinMode(LED_BUILTIN, OUTPUT);
LittleFS.begin();
int numCerts = certStore.initCertStore(LittleFS, PSTR("/certs.idx"), PSTR("/certs.ar"));
Serial.print(F("Number of CA certs read: "));
Serial.println(numCerts);
if (numCerts == 0) {
Serial.println(F("No certs found. Did you run certs-from-mozill.py and upload the LittleFS directory before running?"));
return; // Can't connect to anything w/o certs!
}
//repeatedCall();
FirmwareUpdate();
}
void loop()
{
}
void connect_wifi()
{
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print("O");
}
Serial.println("Connected to WiFi");
}

eBPF packet monitor losing some packets when 'ping -f'ed

I wrote this program to capture all the packets that come into my network interface.
It seems to work fine with things like ping [IP].
It also works fine with ping -f [IP] -c 10.
However, when the number of packets that are pinged goes up to like 200, the program sees to lost some packets.
Is this a natural limit of eBPF or am I doing something wrong?
Here's the code :
Also, when I ping -f [IP] -c 500, it also outputs : "Possibly lost 10 samples" or "Possibly lost 34 samples"
from bcc import BPF
# Network interface to be monoitored
INTERFACE = "my_interface"
bpf_text = """
#include <uapi/linux/ptrace.h>
#include <net/sock.h>
#include <bcc/proto.h>
#include <linux/bpf.h>
#define IP_TCP 6
#define IP_UDP 17
#define IP_ICMP 1
#define ETH_HLEN 14
BPF_PERF_OUTPUT(skb_events); // has to be delcared outside any function
BPF_ARRAY(black_list, u64, 5);
int packet_monitor(struct __sk_buff *skb) {
u8 *cursor = 0;
u32 saddr;
u32 daddr;
u32 ttl;
u32 hchecksum;
u64 magic = 111;
u64 magic2 = 111;
struct ethernet_t *ethernet = cursor_advance(cursor, sizeof(*ethernet));
if (!(ethernet -> type == 0x0800)) {
return 0; // drop
}
struct ip_t *ip = cursor_advance(cursor, sizeof(*ip));
/*
if (ip->nextp != IP_TCP)
{
if (ip -> nextp != IP_UDP)
{
if (ip -> nextp != IP_ICMP)
return 0;
}
}
*/
skb_events.perf_submit_skb(skb, skb -> len, &magic, sizeof(magic));
saddr = ip -> src;
daddr = ip -> dst;
ttl = ip -> ttl;
hchecksum = ip -> hchecksum;
magic = ip -> src;
magic2 = ip -> dst;
skb_events.perf_submit_skb(skb, skb->len, &magic, sizeof(magic)); // this one parses number as a hex to the user space
skb_events.perf_submit_skb(skb, skb->len, &magic2, sizeof(magic2)); // can send multiple values like this
bpf_trace_printk("saddr = %llu, daddr = %llu, ttl = %llu", saddr, daddr, ttl);
// bpf_trace_printk("Incoming packet!!\\n");
return -1;
}
"""
from ctypes import *
import ctypes as ct
import sys
import socket
import os
import struct
def print_skb_event(cpu, data, size):
class SkbEvent(ct.Structure):
_fields_ = [ ("magic", ct.c_uint32), ("magic2", ct.c_uint32)]
skb_event = ct.cast(data, ct.POINTER(SkbEvent)).contents
print("- : ")
print("%d" % (skb_event.magic))
bpf = BPF(text=bpf_text)
function_skb_matching = bpf.load_func("packet_monitor", BPF.SOCKET_FILTER)
BPF.attach_raw_socket(function_skb_matching, INTERFACE)
bpf["skb_events"].open_perf_buffer(print_skb_event)
black_list = bpf.get_table("black_list") # retrieve blacklist list
try:
while True :
bpf.perf_buffer_poll() # value = bpf.perf_buffer_poll() function does not return any function and therefore, doesn't work
except KeyboardInterrupt:
pass
Yes, that's a limitation of the perf ring buffer. If the BPF program produce events on the ring buffer faster than the userspace (Python) process can consume them, some events will be lost (overwritten since it's a ring). The Possibly lost XX samples message is a notification of this happening.
I would first recommend that you try to group your multiple skb_events.perf_submit_skb calls into a single one on the BPF side. That may help. Otherwise, you can try to aggregate data on the BPF side to have less information sent to the Python side.

STM32H743ZI NUCLEO 144 & LWIP - Can't Ping The Board

Hope all is going well.
I'm trying to ping STM32H743ZI NUCLEO 144 using LWIP middle-ware. Code generated by CubeMX.
Configurations:
Set the HCLK to 400 MHz
Enabled the CPU ICache and DCache (under Cortex_M7 Configuration)
Enabled MPU (Region0, Region1 & Region2)
Enabled LWIP
Selected LAN8742 as the Driver_PHY (under LwIP>Platform Settings)
DHCP disabled (IP, MASK: 255,255,255,000 , Gateway: Modem IP)
RTOS disabled
LWIP_HTTPD, LWIP_HTTPD_CGI enabled
LWIP_HTTPD_SSI enabled
LWIP_HTTPD_MAX_TAG_NAME_LEN set to 16
ICMP enabled (LWIP_BROADCAST_PING and LWIP_MULTICAST_PING in LwIP Key Options>IPMP Options).
Code Generated for Keil V5
MX_LWIP_Process added to the main function in While loop.
while(1)
{
MX_LWIP_Process();
}
I don't know how should I configure the CubeMX or change the generated code to be able to ping my board.
My_File
this will probably help you (it did for me):
Information about this issue can be found here.
https://community.st.com/s/article/FAQ-Ethernet-not-working-on-STM32H7x3
Memory buffers need to be assigned to RAM that can be accessed by the Ethernet
peripheral.
You may need to adjust the tour stack/heap size.
The default Ethernet GPIOs speed may be too low.
You may need to configure the MPU.
You may likely need to change your linker script.
On this page you will find good information:
https://github.com/MX-Master/STM32H7_Nucleo-H743ZI_Ethernet_LwIP
The HAL_Delay mentioned may not be required, though.
In the file lan8742.c (driver), I added an extra line for the LAN8742_Init function, around line 190, to set auto-negotiation:
// Link did not come up after HW reset.
pObj->IO.WriteReg(pObj->DevAddr, LAN8742_BCR, LAN8742_BCR_AUTONEGO_EN);
So that function looks like:
// Used in ethernetif.c, 363, static void low_level_init(struct netif *netif)
int32_t LAN8742_Init(lan8742_Object_t *pObj)
{
uint32_t tickstart = 0, regvalue = 0, addr = 0;
int32_t status = LAN8742_STATUS_OK;
if(pObj->Is_Initialized == 0)
{
if(pObj->IO.Init != 0)
{
/* GPIO and Clocks initialization */
pObj->IO.Init();
}
/* for later check */
pObj->DevAddr = LAN8742_MAX_DEV_ADDR + 1;
/* Get the device address from special mode register */
for(addr = 0; addr <= LAN8742_MAX_DEV_ADDR; addr ++)
{
if(pObj->IO.ReadReg(addr, LAN8742_SMR, &regvalue) < 0)
{
status = LAN8742_STATUS_READ_ERROR;
/* Can't read from this device address
continue with next address */
continue;
}
if((regvalue & LAN8742_SMR_PHY_ADDR) == addr)
{
pObj->DevAddr = addr;
status = LAN8742_STATUS_OK;
break;
}
}
if(pObj->DevAddr > LAN8742_MAX_DEV_ADDR)
{
status = LAN8742_STATUS_ADDRESS_ERROR;
}
/* if device address is matched */
if(status == LAN8742_STATUS_OK)
{
/* set a software reset */
if(pObj->IO.WriteReg(pObj->DevAddr, LAN8742_BCR, LAN8742_BCR_SOFT_RESET) >= 0)
{
/* get software reset status */
if(pObj->IO.ReadReg(pObj->DevAddr, LAN8742_BCR, &regvalue) >= 0)
{
tickstart = pObj->IO.GetTick();
/* wait until software reset is done or timeout occurred */
while(regvalue & LAN8742_BCR_SOFT_RESET)
{
if((pObj->IO.GetTick() - tickstart) <= LAN8742_SW_RESET_TO)
{
if(pObj->IO.ReadReg(pObj->DevAddr, LAN8742_BCR, &regvalue) < 0)
{
status = LAN8742_STATUS_READ_ERROR;
break;
}
}
else
{
status = LAN8742_STATUS_RESET_TIMEOUT;
}
}
}
else
{
status = LAN8742_STATUS_READ_ERROR;
}
}
else
{
status = LAN8742_STATUS_WRITE_ERROR;
}
}
}
// Jack 2019-03-25, Link did not come up after HW reset.
pObj->IO.WriteReg(pObj->DevAddr, LAN8742_BCR, LAN8742_BCR_AUTONEGO_EN);
if(status == LAN8742_STATUS_OK)
{
tickstart = pObj->IO.GetTick();
/* Wait for 2s to perform initialization */
while((pObj->IO.GetTick() - tickstart) <= LAN8742_INIT_TO)
{
}
pObj->Is_Initialized = 1;
}
return status;
}

Linux socket hardware timestamping

I'm working on a project researching about network synchronisation. Since I want to achieve the best performance I'm trying to compare software timestamping results with hardware timestamping ones.
I have followed this previously commented issue: Linux kernel UDP reception timestamp but after several tests I got some problems when trying to get hardware reception timestamps.
My scenario is composed of 2 devices, a PC and a Gateworks Ventana board, both devices are supposed to be waiting for packets to be broadcasted in their network and timestamping their reception times, I have tried this code (some parts have been omitted):
int rc=1;
int flags;
flags = SOF_TIMESTAMPING_RX_HARDWARE
| SOF_TIMESTAMPING_RAW_HARDWARE;
rc = setsockopt(sock, SOL_SOCKET,SO_TIMESTAMPING, &flags, sizeof(flags));
rc = bind(sock, (struct sockaddr *) &serv_addr, sizeof(serv_addr));
struct msghdr msg;
struct iovec iov;
char pktbuf[2048];
char ctrl[CMSG_SPACE(sizeof(struct timespec))];
struct cmsghdr *cmsg = (struct cmsghdr *) &ctrl;
msg.msg_control = (char *) ctrl;
msg.msg_controllen = sizeof(ctrl);
msg.msg_name = &serv_addr;
msg.msg_namelen = sizeof(serv_addr);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
iov.iov_base = pktbuf;
iov.iov_len = sizeof(pktbuf);
//struct timeval time_kernel, time_user;
//int timediff = 0;
FILE *f = fopen("server.csv", "w");
if (f == NULL) {
error("Error opening file!\n");
exit(1);
}
fprintf(f, "Time\n");
struct timespec ts;
int level, type;
int i;
for (i = 0; i < 10; i++) {
rc = recvmsg(sock, &msg, 0);
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg))
{
level = cmsg->cmsg_level;
type = cmsg->cmsg_type;
if (SOL_SOCKET == level && SO_TIMESTAMPING == type) {
//ts = (struct timespec *) CMSG_DATA(cmsg);
memcpy(&ts, CMSG_DATA(cmsg), sizeof(ts));
printf("HW TIMESTAMP %ld.%09ld\n", (long)ts.tv_sec, (long)ts.tv_nsec);
}
}
}
printf("COMPLETED\n");
fclose(f);
close(sock);
return 0;
}
In both devices the output I get after receiving a packet:
HW TIMESTAMP 0.000000000
On the other hand if with the same code my flags are:
flags = SOF_TIMESTAMPING_RX_HARDWARE
| SOF_TIMESTAMPING_RX_SOFTWARE
| SOF_TIMESTAMPING_SOFTWARE;
I get proper timestamps:
HW TIMESTAMP 1551721801.970270543
However, they seem to be software-timestamping ones. What would be the correct solution / method to handle hardware timestamping for packets received?
First of all, use ethtool -T "your NIC" to make sure your hardware supports the hardware timestamping feature.
You need to explicitly tell the Linux to enable the hardware timestamping feature of your NIC. In order to to that, you need to have a ioctl() call.
What you have to do is to call it with SIOCSHWTSTAMP, which is a device request code to indicate which device you want to handle as well as what you want to do. For example, there is a code called CDROMSTOP to stop the cdrom drive.
You also need to use a ifreq struct to configure your NIC.
You need something like this:
struct ifreq ifconfig;
strncpy(config.ifr_name, "your NIC name", sizeof(ifconfig.ifr_name));
ioctl("your file descriptor" , SIOCSHWTSTAMP, &ifconfig);
Here are some pages that you can look up to:
ioctl manual page,
ifreq manual page,
Read part 3.

Raspberry error write in i2c socket

I'm trying to control my domotic house by smartphone, the smartphone sends 3-4 byte to Raspberry via Internet(Wi-Fi) and Raspberry send all those bytes to the corresponding Arduino throught I2C bus(I've got two Arduinos).
When I send the commands to Raspberry it shows "Failed to write to the i2c bus"
Anyone can help me please?
int i2csend(msg_t *pmsg)
{
int fd;
/* Open I2C device */
if ((fd = open(device, O_RDWR)) < 0) error ("Can't open I2C device");
if (ioctl(fd, I2C_SLAVE, arduino_addr) < 0) error ("Can't talk to slave");
if (write(fd, (char *)pmsg, n) < n ) printf ("Failed to write to the i2c bus [1]\n");
else
{
read(fd, (char *)pmsg, n);
printf("Ricevuto il messaggio: %c%c %d %d\n", pmsg->tipo, pmsg->gruppo, pmsg->dato[0], pmsg->dato[1]);
}
close(fd);
return 0;
}
When I've used I2C on the raspi, I've never used the 'open' function in an if statement (like you have in the i2csend() function). Here's a sample from a (working) project of mine:
//open file for i2c interface
int fh=open("/dev/i2c-1",O_RDWR);
if (ioctl(fh, I2C_SLAVE, UIBC_ADDR) < 0){
printf("Couldn't establish contact with the UIBC\n");
}