Blog Image

guivi

About the blog

In this blog I will keep track of projects I develop though out this year and may be in the future. For now it is juts a testing ground for developing the blog itself but I hope as I put more material it will become a good place for me to hold information.

Kernel Module

Pi Stuff Posted on 08 Feb, 2019 16:14:55

Real time interrupts and operating systems are not generally compatible. For operating systems it is needed to poll the pin which one is willing to work as an interrupt but this is not real time and generally speaking is not very CPU deficient. For this purpose a Kernel module can be inserted which can handle this as real time as possible. I needed to make an interrupt in a raspberry pi to be able to count encoder signals, the following tutorial is the ins and outs of how I achieved this.

Firstly install the latest Raspbian (I have done this tutorial with Raspbian Stretch Lite) I will also mention here that I recommend using a none X server operating system as some of the print calls from kernel you may not see them if you are in a X server OS:
https://www.raspberrypi.org/downloads/raspbian/

Once downloaded you can burn the image to a micro SD card using DiskImager in windows:
https://sourceforge.net/projects/win32diskimager/

Then insert the SD card on the Raspberry PI (I used PI3) and boot from the micro SD card. Make sure the PI is connected to the internet.

In the command line run the following commands:

# The usual update routine

apt-get update -y
apt-get upgrade -y

# Update the kernel!

rpi-update

# Get rpi-source

sudo wget https://raw.githubusercontent.com/notro/rpi-source/master/rpi-source -O /usr/bin/rpi-source

# Make it executable

sudo chmod +x /usr/bin/rpi-source

# Tell the update mechanism that this is the latest version of the script

/usr/bin/rpi-source -q –tag-update

# Get the kernel files thingies.

rpi-source

At this point you should have all the needed components to start compiling a kernel module.

So lets start by looking at a simple Hello World kernel module:

# Create a new directory enter it.

mkdir hello
cd hello

# Crete a file named hello.c and edit it

nano hello.c

# Edit the file to have the following lines of code in it.

#include <linux/module.h>
#include <linux/kernel.h>

int hello_init(void)
{
pr_alert(“Hello World :)\n”);
return 0;
}

void hello_exit(void)
{
pr_alert(“Goodbye World!\n”);
}
module_init(hello_init);
module_exit(hello_exit);

# Save the file and exit nano

Ctrl+X (and follow the on screen options)

# Make a Makefile for compiling

nano Makefile

# Edit it as follow.

obj-m := hello.o

# save file and exit

Ctrl+X (and follow the on screen options)

# Compile and load kernel module

make -C /lib/modules/$(uname -r)/build M=$(pwd) modules
insmod hello.ko

That is it. You have mounted your first kernel module. it does nothing but it is a start.

——————————————————————————————————
——————————————————————————————————
————————- SOMETHING A BIT MORE COMPLICATED —————————
——————————————————————————————————
——————————————————————————————————

The interrupt module I made looks like this:

#include <linux/device.h>
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/time.h>
#include <linux/fs.h>
#include <linux/uaccess.h>

#define GPIO_FOR_RX_SIGNAL 27
#define GPIO_FOR_INT_SIGNAL_2 17
#define DEV_NAME “rfrpi”
#define BUFFER_SZ 512

#ifndef IRQF_DISABLED
#define IRQF_DISABLED 0
#endif

/* Last Interrupt timestamp */
static struct timespec lastIrq_time;
static unsigned long lastDelta[BUFFER_SZ];
static int pRead;
static int pWrite;
static int wasOverflow;

static int gCounter = 0;

/* Define GPIOs for RX signal */
static struct gpio signals[] = {
{ GPIO_FOR_RX_SIGNAL, GPIOF_IN, “RX Signal” }, // Rx signal
{ GPIO_FOR_INT_SIGNAL_2, GPIOF_IN, “Int 2” },
};

/* Later on, the assigned IRQ numbers for the buttons are stored here */
static int rx_irqs[] = { -1, -1 };

/*
* The interrupt service routine called on every pin status change
*/
static irqreturn_t rx_isr(int irq, void *data)
{
struct timespec current_time;
struct timespec delta;
unsigned long ns;

getnstimeofday(&current_time);
delta = timespec_sub(current_time, lastIrq_time);
ns = ((long long)delta.tv_sec * 1000000)+(delta.tv_nsec/1000);
lastDelta[pWrite] = ++gCounter;//;ns;
getnstimeofday(&lastIrq_time);

pWrite = ( pWrite + 1 ) & (BUFFER_SZ-1);
if (pWrite == pRead) {
// overflow
pRead = ( pRead + 1 ) & (BUFFER_SZ-1);
if ( wasOverflow == 0 ) {
printk(KERN_ERR “RFRPI – Buffer Overflow – IRQ will be missed”);
wasOverflow = 1;
}
} else {
wasOverflow = 0;
}
return IRQ_HANDLED;
}

/*
* The interrupt service routine called on every pin status change
*/
static irqreturn_t int2_isr(int irq, void *data)
{
if (gpio_get_value(signals[0].gpio))
lastDelta[pWrite] = –gCounter;//;ns;
else
lastDelta[pWrite] = ++gCounter;//;ns;
getnstimeofday(&lastIrq_time);

pWrite = ( pWrite + 1 ) & (BUFFER_SZ-1);
if (pWrite == pRead) {
// overflow
pRead = ( pRead + 1 ) & (BUFFER_SZ-1);
if ( wasOverflow == 0 ) {
printk(KERN_ERR “RFRPI – Buffer Overflow – IRQ will be missed”);
wasOverflow = 1;
}
} else {
wasOverflow = 0;
}
return IRQ_HANDLED;
}

static int rx433_open(struct inode *inode, struct file *file)
{
return nonseekable_open(inode, file);
}

static int rx433_release(struct inode *inode, struct file *file)
{
return 0;
}

static ssize_t rx433_write(struct file *file, const char __user *buf,
size_t count, loff_t *pos)
{
return -EINVAL;
}

static ssize_t rx433_read(struct file *file, char __user *buf,
size_t count, loff_t *pos)
{
// returns one of the line with the time between two IRQs
// return 0 : end of reading
// return >0 : size
// return -EFAULT : error
char tmp[256];
int _count;
int _error_count;

_count = 0;
if ( pRead != pWrite ) {
sprintf(tmp,”%ld\n”,lastDelta[pRead]);
_count = strlen(tmp);
_error_count = copy_to_user(buf,tmp,_count+1);
if ( _error_count != 0 ) {
printk(KERN_ERR “RFRPI – Error writing to char device”);
return -EFAULT;
}
pRead = (pRead + 1) & (BUFFER_SZ-1);
}
return _count;
}

static struct file_operations rx433_fops = {
.owner = THIS_MODULE,
.open = rx433_open,
.read = rx433_read,
.write = rx433_write,
.release = rx433_release,
};

static struct miscdevice rx433_misc_device = {
.minor = MISC_DYNAMIC_MINOR,
.name = DEV_NAME,
.fops = &rx433_fops,
};

/*
* Module init function
*/
static int __init rfrpi_init(void)
{
int ret = 0;
printk(KERN_INFO “%s\n”, __func__);

// INITIALIZE IRQ TIME AND Queue Management
getnstimeofday(&lastIrq_time);
pRead = 0;
pWrite = 0;
wasOverflow = 0;

// register GPIO PIN in use
ret = gpio_request_array(signals, ARRAY_SIZE(signals));

if (ret) {
printk(KERN_ERR “RFRPI – Unable to request GPIOs for RX Signals: %d\n”, ret);
goto fail2;
}

// Register IRQ for this GPIO
ret = gpio_to_irq(signals[0].gpio);
if(ret < 0) {
printk(KERN_ERR “RFRPI – Unable to request IRQ: %d\n”, ret);
goto fail2;
}
rx_irqs[0] = ret;
printk(KERN_INFO “RFRPI – Successfully requested RX IRQ # %d\n”, rx_irqs[0]);
ret = request_irq(rx_irqs[0], rx_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_DISABLED, “rfrpi#rx”, NULL);
if(ret) {
printk(KERN_ERR “RFRPI – Unable to request IRQ: %d\n”, ret);
goto fail3;
}

// Register IRQ for second GPIO
ret = gpio_to_irq(signals[1].gpio);
if(ret < 0) {
printk(KERN_ERR “RFRPI – Unable to request IRQ: %d\n”, ret);
goto fail2;
}
rx_irqs[1] = ret;
printk(KERN_INFO “RFRPI – Successfully requested RX IRQ # %d\n”, rx_irqs[1]);
ret = request_irq(rx_irqs[1], int2_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_DISABLED, “rfrpi#rx”, NULL);
if(ret) {
printk(KERN_ERR “RFRPI – Unable to request IRQ: %d\n”, ret);
goto fail3;
}

// Register a character device for communication with user space
misc_register(&rx433_misc_device);

return 0;

// cleanup what has been setup so far
fail3:
free_irq(rx_irqs[0], NULL);
free_irq(rx_irqs[1], NULL);

fail2:
gpio_free_array(signals, ARRAY_SIZE(signals));
return ret;
}

/**
* Module exit function
*/
static void __exit rfrpi_exit(void)
{
printk(KERN_INFO “%s\n”, __func__);

misc_deregister(&rx433_misc_device);

// free irqs
free_irq(rx_irqs[0], NULL);
free_irq(rx_irqs[1], NULL);

// unregister
gpio_free_array(signals, ARRAY_SIZE(signals));
}

MODULE_LICENSE(“GPL”);
MODULE_AUTHOR(“Disk91”);
MODULE_DESCRIPTION(“Linux Kernel Module for rfrpi shield”);

module_init(rfrpi_init);
module_exit(rfrpi_exit);

The Makefile for it is as follow:

ifneq (${KERNELRELEASE},)
obj-m = krfrpi.o
else
KERNEL_DIR ?= /lib/modules/$(shell uname -r)/build
MODULE_DIR := $(shell pwd)

.PHONY: all

all: modules

.PHONY:modules

modules:
${MAKE} -C ${KERNEL_DIR} SUBDIRS=${MODULE_DIR} modules

clean:
rm -f *.o *.ko *.mod.c .*.o .*.ko .*.mod.c .*.cmd *~
rm -f Module.symvers Module.markers modules.order
rm -rf .tmp_versions
endif

Once this module is mounted it makes a file at /dev/rfrpi this file can be dumped with cat /dev/rfrpi or you can read it in a c program as follow:

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>

int main(int argc, char ** argv) {
int fd;
char buf[256];

fd = open(“/dev/rfrpi”, O_RDWR | O_NOCTTY | O_NDELAY);
if (fd == -1) {
perror(“open_port: Unable to open /dev/rfrpi – “);
return(-1);
}

// Turn off blocking for reads, use (fd, F_SETFL, FNDELAY) if you want that
fcntl(fd, F_SETFL, 0);

while(1){
int n = read(fd, (void*)buf, 255);
if (n < 0) {
perror(“Read failed – “);
return -1;
} else if (n == 0) {
printf(“No data on port\n”);
sleep(1);
}
else {
buf[n] = ‘\0’;
printf(/*”%i bytes read : %s” */”%15s”/*, n*/, buf);
}
//usleep(1000);
sleep(1);
//printf(“i’m still doing something”);

}
close(fd);
return 0;
}

References:

Step by step on how to compile a kernel module:
https://raspberrypi.stackexchange.com/questions/39845/how-compile-a-loadable-kernel-module-without-recompiling-kernel#40419

Raspberry PI Interrupt example:
https://www.disk91.com/2015/technology/systems/rf433-raspberry-pi-gpio-kernel-driver-for-interrupt-management/

Article on Raspberry PI module kernel programming:
https://blog.fazibear.me/the-beginners-guide-to-linux-kernel-module-raspberry-pi-and-led-matrix-790e8236e8e9

Information on GPIO.h header file. Very good for general reference.
https://www.mjmwired.net/kernel/Documentation/gpio.txt

https://elixir.bootlin.com/linux/latest/source/include/linux/gpio.h



Compiling RaspiVid Only

Pi Stuff Posted on 20 Jun, 2018 15:40:09

To build raspivid and only raspivid then the following command should do it (assuming your repo (https://github.com/raspberrypi/userland.git) clone is in ~/userland):

gcc -o myraspivid RaspiVid.c RaspiCamControl.c RaspiPreview.c RaspiCLI.c \ -I$HOME/userland -I$HOME/userland/host_applications/linux/libs/bcm_host/include \ -L/opt/vc/lib -lbcm_host -lvcos -lpthread -lmmal_core -lmmal_util -lmmal_vc_client



Streaming Video from raspberry PI

Pi Stuff Posted on 20 Jun, 2018 14:22:09

tested with

  • RasPi 3B with Raspbian Jessie, IPv4 address 192.168.2.104
  • Ubuntu 16.04 client, IPv4 address 192.168.2.108

raspivid options used:

option explanation
-pf baseline use baseline profile (no B-frames) which reduces delay
-b 1000000 bitrate (1MBit/s)
-g 30 GOP (group of pictures) size of 30, i.e. send an I-frame every second
-ih inline headers: send SPS and PPS headers with each I-frame


live TCP streaming on RasPi

RasPi is listening (-l) and keeps (-k) listening after a TCP session terminated. RasPi’s own (local) IPv4 address is specified.

pi@raspberrypi:~ $ raspivid -t 0 -n -b 1000000 -g 30 -ih -pf baseline -w 640 -h 480 -fps 30 -l -o tcp://192.168.2.104:1234

Drawback: after client terminates, raspivid terminates too. So you might want to wrap it with an endless bash script loop

pi@raspberrypi:~ $ while :; do raspivid -t 0 -n -b 1000000 -g 30 -ih -pf baseline -w 640 -h 480 -fps 30 -l -o tcp://192.168.2.104:1234 ; done

remote client

Now, on remote client, vlc is opening and terminating TCP stream:

$ vlc -v tcp/h264://192.168.2.104:1234

UDP streaming remote client

Start remote client first, otherwise it could miss the H.264 SPS/PPS which are needed for decoding the stream. Client is waiting for incoming packets on UDP socket:

$ vlc -vvv udp/h264://@:1234

RasPi

RasPi is pushing UDP packets to the client.

without nc

pi@raspberrypi:~ $ raspivid -t 0 -n -b 1000000 -g 30 -ih -pf baseline -w 640 -h 480 -fps 30 -o udp://192.168.2.108:1234

while client port is not available, you see error messages like mmal: Failed to write buffer data (3294 from 14539)- aborting but they do not do any harm.

with nc

I think, this is no longer needed.

pi@raspberrypi:~ $ raspivid -t 0 -n -b 1000000 -g 30 -ih -pf baseline -w 640 -h 480 -fps 30 -o – | nc -p 1904 -u 192.168.2.108 1234

passing video to

pi@raspberrypi:~ $ raspivid -t 10000 -n -b 1000000 -g 30 -ih -pf baseline -w 1920 -h 1080 -fps 30 -o – | gst-launch-1.0 fdsrc ! ‘video/x-h264,profile=baseline, width=1920, height=1080’ ! h264parse ! mp4mux ! filesink location=test.mp4

RTSP streaming

there is a session concept + time stamping (RTP) included. several transport protocols are possible: UDP, TCP and HTTP tunnelled.

RTP/UDP

# raspivid -t 0 -n -b 1000000 -g 30 -ih -pf baseline -w 640 -h 480 -fps 30 -o – | cvlc -v stream:///dev/stdin –sout ‘#rtp{sdp=rtsp://:8554/}’ :demux=h264

$ vlc -v rtsp://192.168.2.104:8554/

further steps

RTSP streaming has many more options like:

  • http tunnelling to stream through a firewall
  • specifying user and password for stream protection

there are alternative RTSP streaming servers out like:

  • live555
  • gst-rtsp-server



Convert Pi into a WiFi Hot Spot

Pi Stuff Posted on 10 Oct, 2017 16:42:32

There’s a new Raspberry Pi. This is exciting. It also has on-board WiFi. This makes it doubly exciting!

One of my first thoughts was, can I use it as a SoftAP for some ESP8266 sensor nodes? As it turns out, you can, and it’s not that difficult, as the BCM43438chip is supported by the open-source brcmfmac driver!

PACKAGES

The first step is to install the required packages: sudo apt-get install dnsmasq hostapd

I’ll go into a little detail about the two:

  • hostapd – This is the package that allows you to use the built in WiFi as an access point
  • dnsmasq – This is a combined DHCP and DNS server that’s very easy to configure

If you want something a little more ‘heavyweight’, you can use the isc-dhcp-server and bind9 packages for DHCP and DNS respectively, but for our purposes, dnsmasq works just fine.

CONFIGURE YOUR INTERFACES

The first thing you’ll need to do is to configure your wlan0 interface with a static IP.

If you’re connected to the Pi via WiFi, connect via ethernet/serial/keyboard first.

In newer Raspian versions, interface configuration is handled by dhcpcd by default. We need to tell it to ignore wlan0, as we will be configuring it with a static IP address elsewhere. So open up the dhcpcd configuration file with sudo nano /etc/dhcpcd.conf and add the following line to the bottom of the file:

denyinterfaces wlan0

Note: This must be ABOVE any interface lines you may have added!

Now we need to configure our static IP. To do this open up the interface configuration file with sudo nano /etc/network/interfaces and edit the wlan0section so that it looks like this:

allow-hotplug wlan0
iface wlan0
inet static
address 172.24.1.1
netmask 255.255.255.0
network 172.24.1.0
broadcast 172.24.1.255 # wpa-conf /etc/wpa_supplicant/wpa_supplicant.conf

Restart dhcpcd with sudo service dhcpcd restart and then reload the configuration for wlan0 with sudo ifdown wlan0; sudo ifup wlan0.

CONFIGURE HOSTAPD

Next, we need to configure hostapd. Create a new configuration file with sudo nano /etc/hostapd/hostapd.conf with the following contents:

# This is the name of the WiFi interface we configured above
interface=wlan0
# Use the nl80211 driver with the brcmfmac driver
driver=nl80211
# This is the name of the network
ssid=Pi3-AP
# Use the 2.4GHz band
hw_mode=g
# Use channel 6
channel=6
# Enable 802.11n
ieee80211n=1
# Enable WMM
wmm_enabled=1
# Enable 40MHz channels with 20ns guard interval
ht_capab=[HT40][SHORT-GI-20][DSSS_CCK-40]
# Accept all MAC addresses
macaddr_acl=0
# Use WPA authentication
auth_algs=1
# Require clients to know the network name
ignore_broadcast_ssid=0
# Use WPA2
wpa=2
# Use a pre-shared key
wpa_key_mgmt=WPA-PSK
# The network passphrase
wpa_passphrase=raspberry
# Use AES, instead of TKIP
rsn_pairwise=CCMP

We can check if it’s working at this stage by running sudo /usr/sbin/hostapd /etc/hostapd/hostapd.conf. If it’s all gone well thus far, you should be able to see to the network Pi3-AP! If you try connecting to it, you will see some output from the Pi, but you won’t receive and IP address until we set up dnsmasq in the next step. Use Ctrl+C to stop it.

We aren’t quite done yet, because we also need to tell hostapd where to look for the config file when it starts up on boot. Open up the default configuration file with sudo nano /etc/default/hostapd and find the line #DAEMON_CONF=”” and replace it with DAEMON_CONF=”/etc/hostapd/hostapd.conf”.

CONFIGURE DNSMASQ

The shipped dnsmasq config file contains a wealth of information on how to use it, but the majority of it is largely redundant for our purposes. I’d advise moving it (rather than deleting it), and creating a new one with

sudo mv /etc/dnsmasq.conf /etc/dnsmasq.conf.orig
sudo nano /etc/dnsmasq.conf

Paste the following into the new file:

interface=wlan0 # Use interface wlan0
listen-address=172.24.1.1 # Explicitly specify the address to listen on
bind-interfaces # Bind to the interface to make sure we aren’t sending things elsewhere
server=8.8.8.8 # Forward DNS requests to Google DNS
domain-needed # Don’t forward short names
bogus-priv # Never forward addresses in the non-routed address spaces.
dhcp-range=172.24.1.50,172.24.1.150,12h # Assign IP addresses between 172.24.1.50 and 172.24.1.150 with a 12 hour lease time

SET UP IPV4 FORWARDING

One of the last things that we need to do before we send traffic anywhere is to enable packet forwarding.

To do this, open up the sysctl.conf file with sudo nano /etc/sysctl.conf, and remove the # from the beginning of the line containing net.ipv4.ip_forward=1. This will enable it on the next reboot, but because we are impatient, activate it immediately with :

sudo sh -c “echo 1 > /proc/sys/net/ipv4/ip_forward”

We also need to share our Pi’s internet connection to our devices connected over WiFi by the configuring a NAT between our wlan0 interface and our eth0interface. We can do this using the following commands:

sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
sudo iptables -A FORWARD -i eth0 -o wlan0 -m state –state
RELATED,ESTABLISHED -j ACCEPT
sudo iptables -A FORWARD -i wlan0 -o eth0 -j ACCEPT

However, we need these rules to be applied every time we reboot the Pi, so run sudo sh -c “iptables-save > /etc/iptables.ipv4.nat” to save the rules to the file /etc/iptables.ipv4.nat. Now we need to run this after each reboot, so open the rc.local file with sudo nano /etc/rc.local and just above the line exit 0, add the following line:

iptables-restore < /etc/iptables.ipv4.nat

WE’RE ALMOST THERE!

Now we just need to start our services:

sudo service hostapd start
sudo service dnsmasq start

And that’s it! You should now be able to connect to the internet through your Pi, via the on-board WiFi!

To double check we have got everything configured correctly, reboot with sudo reboot.

This was literally taken from:
https://frillip.com/using-your-raspberry-pi-3-as-a-wifi-access-point-with-hostapd/

I am coping it here just in case the other place desperate. I value this post that much.