diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..09d9898 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +/ltximg/ +/figures/*.aux +/figures/*.fdb_latexmk +/figures/*.fls +/figures/*.log diff --git a/02-install-contiki.org b/02-install-contiki.org new file mode 100644 index 0000000..cf797d2 --- /dev/null +++ b/02-install-contiki.org @@ -0,0 +1,359 @@ +#+SETUPFILE: https://fniessen.github.io/org-html-themes/org/theme-readtheorg.setup +#+title: 7062CEM Install Contiki +#+Author: James Brusey +#+Email: james.brusey@coventry.ac.uk +#+REVEAL_INIT_OPTIONS: width:1200, height:800, margin: 0.1, minScale:0.2, maxScale:2.5, transition:'cube', slideNumber:true +#+REVEAL_THEME: white +#+REVEAL_HLEVEL: 2 +#+REVEAL_HEAD_PREAMBLE: +#+latex_header: \usepackage[osf]{mathpazo} +#+latex_header: \usepackage{booktabs} +* Learning outcomes + ++ Be able to create a VM instance for Contiki + ++ Run a basic hello-world program under Contiki + +* First steps + +- You might need [[https://git-scm.com/downloads][git]] under Windows (~sudo apt-get~ is for Linux) + +- You will also need [[https://www.virtualbox.org/wiki/Downloads][VirtualBox]] (including the extension pack) and [[https://www.vagrantup.com/downloads.html][Vagrant]] + +- Follow the instructions at https://github.com/contiki-ng/contiki-ng/wiki for Vagrant + +* What is VirtualBox? + +- VirtualBox allows you to run Virtual Machines (VMs) hosted underneath your current operating system + +- The guest operating system (inside the VM) thinks it has full access to a whole computer + +- In fact, some parts are substituted + + - e.g., a host file contains the guest's `hard-disk' + +- Some parts are provided optionally + + - e.g., access to USB ports + +- Often, the VM is run `headless', which means it's not given a display + +* What is Vagrant? + +- Vagrant is an open-source software configuration management tool written in Ruby + +- Vagrant’s configuration for a particular development environment sits in a file called Vagrantfile + +- Vagrant is controlled by command line instructions + +- =vagrant -h= - to get a list of commands (such as =vagrant up=) + +- =vagrant up -h= - to get specific help on the up sub-command + +* Performing the installation + +** The first step is to clone (download) contiki-ng + +You may want to disable =autocrlf= for git using: +#+BEGIN_SRC +git config --global core.autocrlf false +#+END_SRC + +This creates a copy of the whole contiki repository on your local disk. + +#+BEGIN_SRC +C:\Users\xxx> git clone https://github.com/contiki-ng/contiki-ng.git +Cloning into 'contiki-ng'... +remote: Enumerating objects: 29, done. +remote: Counting objects: 100% (29/29), done. +remote: Compressing objects: 100% (23/23), done. +remote: Total 129899 (delta 8), reused 13 (delta 5), pack-reused 129870 +Receiving objects: 100% (129899/129899), 76.77 MiB | 6.65 MiB/s, done. +Resolving deltas: 100% (95688/95688), done. +#+END_SRC + +Instructions for doing a git clone with no change to line endings. + +** Step 2 starting the VM + +The next step will download the guest operating system (Ubuntu) and start it inside a VM + +Need to change to the correct directory first! (It contains =Vagrantfile=) +#+BEGIN_SRC +C:\Users\xxx> vagrant up +Bringing machine 'default' up with 'virtualbox' provider... +==> default: Box 'ubuntu/bionic64' could not be found. Attempting to find and install... +#+END_SRC + +- if you are having problem here, it may be due to your BIOS settings not allowing virtualisation +- see [[https://bce.berkeley.edu/enabling-virtualization-in-your-pc-bios.html]] + +- additionally, it is often helpful to install the =vagrant-vbguest= plugin +#+BEGIN_SRC +$ vagrant plugin install vagrant-vbguest +#+END_SRC + +** Step 3 login to the VM using SSH + +#+BEGIN_SRC +C:\Users\xxx> vagrant ssh +... +vagrant@ubuntu-bionic:~$ +#+END_SRC + +- at this point, this is a vanilla Ubuntu image + +- vagrant sets up a /share/ between the guest and host + +** Step 4 run the bootstrap script + +Check for correct line endings by + +#+BEGIN_SRC +$ od -c ./contiki-ng/tools/vagrant/bootstrap.sh | head +#+END_SRC + +and check for the line endings being =\n= + +#+BEGIN_SRC +$ sudo ./contiki-ng/tools/vagrant/bootstrap.sh +#+END_SRC + +- note that you may need to convert the bootstrap line endings first + +- =sudo= tells the command to run with root privileges + +** Step 5 reboot the VM +#+BEGIN_SRC +$ exit +... + +C:\Users\xxx> vagrant reload +#+END_SRC +(directory here is probably wrong!) + +- Check to see that it comes up ok and in particular watch for this ... + +#+BEGIN_SRC +==> default: Mounting shared folders... +#+END_SRC + +- If shared folders are not mounted then you won't be able to run the next step. + +** Step 6 log back in and try it out + +[[https://github.com/contiki-ng/contiki-ng/wiki/Tutorial:-Hello,-World!]] + +#+BEGIN_SRC +C:> vagrant ssh +... +$ cd contiki-ng/examples/hello-world/ +$ make TARGET=native +$ ./hello-world.native +#+END_SRC + +You should get the following output: + +#+BEGIN_SRC +[WARN: Tun6 ] Failed to open tun device (you may be lacking permission). Running without network. +[INFO: Main ] Starting Contiki-NG-release/v4.6-54-g425587de4 +[INFO: Main ] - Routing: RPL Lite +[INFO: Main ] - Net: tun6 +[INFO: Main ] - MAC: nullmac +[INFO: Main ] - 802.15.4 PANID: 0xabcd +[INFO: Main ] - 802.15.4 Default channel: 26 +[INFO: Main ] Node ID: 1800 +[INFO: Main ] Link-layer address: 0102.0304.0506.0708 +[INFO: Main ] Tentative link-local IPv6 address: fe80::302:304:506:708 +[INFO: Native ] Added global IPv6 address fd00::302:304:506:708 +Hello, world +#+END_SRC + +- The network errors are normal when running natively. + +* Accessing our server +Please note that this section is under revision due to moving the server to a different location and new firewall rules at Coventry University. +It will be updated as soon as we have a new server available for you. + +** Logging into the server +We are also providing access to a VM with contiki pre-installed +#+BEGIN_SRC +ssh -p 2222 xxx@cogentee.hopto.org +#+END_SRC +where ~xxx~ is the username that you have been provided with. + +- You should also have been given a password. + +** What it looks like + +#+BEGIN_SRC +The authenticity of host '[cogentee.hopto.org]:2222 ([213.106.109.33]:2222)' can't be established. +ECDSA key fingerprint is SHA256:7jVYKsBTdPqZkEReCdRn/Dd/n9BFjNeTWdP2+HpvUKI. +Are you sure you want to continue connecting (yes/no/[fingerprint])? yes + +Warning: Permanently added '[cogentee.hopto.org]:2222,[213.106.109.33]:2222' (ECDSA) to the list of known hosts. +xxx@cogentee.hopto.org's password: +xxx@ubuntu-bionic:~$ +#+END_SRC + +** Trying it out + +First make a copy of the contiki directory (only need to do this once) + +#+BEGIN_SRC +$ git clone https://github.com/contiki-ng/contiki-ng.git +$ cd contiki-ng +$ git submodule update --init --recursive +#+END_SRC + +Possibly also do the submodules clone here? + +Then you should be able to compile the hello-world example as before +#+BEGIN_SRC +$ cd contiki-ng/examples/hello-world/ +$ make TARGET=native +$ ./hello-world.native +#+END_SRC + +** Editing your files + +To edit the file, you need to use a text editor, such as =nano=, =vi=, or =emacs=. + +#+BEGIN_SRC +nano hello-world.c +#+END_SRC + +[[file:figures/nano.png]] + +- =nano= is pretty basic though! +- You can't use the mouse, you have to move around with the arrow keys. + +* Transferring files + +When you come to hand in your assignment, you'll want to be able to transfer files to or from the remote server + +One way to do this is =scp= source target + +The following copies =hello-world.c= to our current directory: +#+BEGIN_SRC +C:\Users\xxx> scp -P 2222 xxx@cogentee.hopto.org:contiki-ng/examples/hello-world.c . +#+END_SRC + +The following copies =hello-world.c= to the remote: +#+BEGIN_SRC +C:\Users\xxx> scp -P 2222 hello-world.c xxx@cogentee.hopto.org:contiki-ng/examples/ +#+END_SRC + + +* Using GNU Emacs and tramp (optional) + +- GNU Emacs is a very powerful open source editor +- Spacemacs or Doom Emacs package up some of the nicer add-ons +- We are going to show the use of TRAMP + +[[file:figures/emacs-spacemacs.png]] +[[file:figures/doom-emacs.png]] + + +** Installing GNU Emacs on Windows + +- There are many guides for this but you might try [[http://mdr78.github.io/2019/07/01/windows-spacemacs-install.html]] + +** Using TRAMP + +- Use C-x C-f to start opening a file +- At the prompt, enter + +#+BEGIN_SRC +/ssh:xxx@cogentee.hopto.org#2222: +#+END_SRC + +- =/ssh:= indicates the protocol to use + +- =xxx@= is the username followed by `@' + +- =cogentee.hopto.org= is the hostname + +- =#2222= means port 2222 + +- Pay attention to the leading =/= and ending =:= to avoid this being misinterpreted as a normal file name + +- This should prompt for your password and then show you a directory listing + +- At the bottom `mode line' you should see something like: + +[[file:figures/tramp-modeline.png]] + +- The `@' sign indicates a remote file location + +- You can now open a file by moving the cursor to it and pressing enter + +- When you save the file =C-x C-s=, the revised contents are sent across the 'net. + +- With a remote file open, use =M-x= (alt key plus `x') to enter an emacs command and type +#+BEGIN_SRC +shell +#+END_SRC +to bring up a shell prompt + +- You can swap between files using =C-x b RET= + +* Using Virtual Network Computing (VNC) + +VNC allows you to access a virtual desktop environment on a remote server. + +There are a few steps to getting this working. + +[[file:figures/vnc_viewer.png]] + +** Identify the port number + +We have allocated 1 port number per student with a number between 5902 and 5912 inclusive. For the example below, I've used 5901 but you'll need to change that to your own number. + +** SSH Tunneling +- Most IP ports are hidden by the firewall (including port 5901) + +- SSH provides a way to access these ports using an [[https://help.ubuntu.com/community/SSH/OpenSSH/PortForwarding][/ssh tunnel/]]. +#+BEGIN_SRC + -------------------- -------------------- + | Local | | Remote | + | SSH----22<=======>22---SSH | + | -------- | | | | -------- | + | | VNC |<=>5901 | | 5901<=>| VNC | | + | | Viewer | | | | Server | | + | | | | | | | | + | -------- | | -------- | + -------------------- -------------------- +#+END_SRC +*** SSH tunneling using command line + +- To set up a tunnel, enter the following command from a PowerShell prompt, changing =xxx= for your username: + +#+BEGIN_SRC +ssh -p 2222 -L 5901:localhost:5901 xxx@cogentee.hopto.org +#+END_SRC + +- Note that you need to leave the connection open to keep the tunnel running. + +*** SSH tunneling using putty + +- If you prefer to use [[https://www.putty.org][putty]] for ssh, you can set a tunnel up using instructions from [[https://blog.devolutions.net/2017/4/how-to-configure-an-ssh-tunnel-on-putty]] + + - An example configuration for port 5901 can be seen below. This needs to be done after the correct session information is set. +[[file:figures/ssh_putty.png]] + +** Running VNC Viewer + +- Download VNC Viewer from : [[https://www.realvnc.com/en/connect/download/viewer/]] + +- Use the appropriate vnc viewer for your operating system (has support for Windows, Mac, Linux and even ChromeOS) + +- Add a 'New Connection' and input =localhost:5901= (change this!), to the VNC Server Field. + +[[file:figures/vnc-localhost.png]] + +* Summary + +- We've covered installing Contiki from scratch and getting a hello world program running + +- We've also covered how to use our server with =ssh=, =tramp=, and =vnc= diff --git a/04-contiki-overview.org b/04-contiki-overview.org new file mode 100644 index 0000000..3db54d8 --- /dev/null +++ b/04-contiki-overview.org @@ -0,0 +1,327 @@ +#+title: 7062CEM Contiki-NG Overview +#+Author: James Brusey +#+Email: j.brusey@coventry.ac.uk +#+Options: num:nil toc:nil +#+REVEAL_INIT_OPTIONS: width:1200, height:1200, margin: 0.1, minScale:0.2, maxScale:2.5, transition:'cube', slideNumber:true +#+REVEAL_THEME: white +#+REVEAL_HLEVEL: 1 +#+REVEAL_HEAD_PREAMBLE: +#+latex_header: \usepackage[osf]{mathpazo} +#+latex_header: \usepackage{booktabs} +#+latex_header: \usepackage{array} +#+latex_header: \newcolumntype{L}[1]{>{\raggedright\arraybackslash}p{#1\columnwidth}} +#+latex_header: \newcolumntype{C}[1]{>{\centering\arraybackslash}p{#1\columnwidth}} +#+latex_header: \newcolumntype{R}[1]{>{\raggedleft\arraybackslash}p{#1\columnwidth}} + +* Learning outcomes +- Familiarity with Contiki-NG basics +- Be able to list the key features of Contiki-NG +- Know how to compile a simple application + +* What is Contiki? +- Light-weight, open-source OS for IoT. +- Connects tiny low-cost, low-power microcontrollers to the Internet. +- Powerful tool for building complex wireless applications + - Remote house monitoring, intrusion detection, industrial monitoring, streetlights, and so on. + +* Contiki or Contiki-NG? +- We are using Contiki-NG throughout +- Contiki-NG is a fork from Contiki from 2017 +- Focus is on reliability, better documentation, and more regular release cycles +- However, pretty much everything that we are doing can also be done with vanilla Contiki + +* Key features + +** Rapid development +- Standard C +- Cooja simulator +- Instant Contiki (we're not using this as it is quite old) +- Open-source and active community + +** Full IP networking +- Fully standard IPv4 and IPv6 +- Support for standard IP protocols: TCP, UDP, HTTP, MQTT +- Support for low-power wireless standards: 6lowpan, RPL and CoAP + +** Rime stack +- If bandwidth is limited or full IPv6 stack is an overkill +- Supports simple operations such as unicast, broadcast, flooding, and address-free multi-hop + +** A selection of hardware +- Range of low-power, wireless devices +- Zolertia Firefly motes + [[file:figures/zolertia-firefly.png]] +** Zolertia Firefly Mote +- Zoul module + - CC2538 ARM Cortex-M3 + - 2 on-board IEEE802.15.4-compliant transceivers + - 2.4GHz and 863-950MHz + - 512KB programmable flash +- Low-power IoT development platform + - Active state: 20mA; Idle state (3 modes) : 0.4uA - 0.6mA +- On-board pin connectors: SPI and I2C interface support + +** Protothreads +- Low overhead mechanism for concurrent programming +- Mixture of event-driven and multi-threaded programming mechanisms +- Each Contiki-NG process is a protothread + +** Coffee flash file system +- Open, read, write, append and close functions on files on the external flash + +** Power awareness +- Mechanisms to estimate and understand the power consumption of the system +- Enable motes to run for years on AA batteries + +** Dynamic Module Loading +- Supports dynamic linking and loading of modules at run-time +- Over-the-air programming + +** Build system +- Make files +- Easy to compile on any supported hardware + +** Examples +- =contiki/examples= +- iot-workshop/examples/zolertia + +* Getting started with Contiki-NG + +We are using the vagrant image but there are several other options (including running native on Windows). + + +** Contiki-NG structure: Exploring Contiki +Directory structure +#+BEGIN_SRC sh +contiki-ng/ + arch # architecture dependent + examples # generic examples + os # main contiki operating system + tests # test code + tools # tools for users (including cooja) +#+END_SRC +See https://github.com/contiki-ng/contiki-ng/wiki/Repository-structure + +** Hello World! + +#+BEGIN_SRC c +#include "contiki.h" + +#include /* For printf() */ +/*---------------------------------------------------------------------------*/ +PROCESS(hello_world_process, "Hello world process"); +AUTOSTART_PROCESSES(&hello_world_process); +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(hello_world_process, ev, data) +{ + static struct etimer timer; + + PROCESS_BEGIN(); + + /* Setup a periodic timer that expires after 10 seconds. */ + etimer_set(&timer, CLOCK_SECOND * 10); + + while(1) { + printf("Hello, world\n"); + + /* Wait for the periodic timer to expire and then restart the timer. */ + PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&timer)); + etimer_reset(&timer); + } + + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ + +#+END_SRC + +** Makefile +#+BEGIN_SRC makefile +CONTIKI_PROJECT = hello-world +all: $(CONTIKI_PROJECT) + +CONTIKI = ../.. +include $(CONTIKI)/Makefile.include + +#+END_SRC + +* Connect the mote +1. When physically connecting the mote to your laptop, be careful of the USB connector + - e.g. use an extender, hub, or prop the laptop on a book to make sure that the connector doesn't break +2. Since we are using a VM, we need to give the VM access to the physical device. + - it is possible to do this in the gui (but error prone) + - to make sure you get the right settings, edit Vagrant file and restart the VM + #+BEGIN_SRC ruby + config.vm.provider "virtualbox" do |vb| + vb.customize ["modifyvm", :id, "--usb", "on", + "--usbxhci", "on", + "--paravirtprovider", "kvm"] + vb.customize ["usbfilter", "add", "0", + "--target", :id, + "--name", "MTM-CM5000MSP", + "--vendorid", "0x0403", + "--productid", "0x6001"] + # # Display the VirtualBox GUI when booting the machine + # vb.gui = true + # + # # Customize the amount of memory on the VM: + # vb.memory = "1024" + end +#+END_SRC +3. Check that the mote has connected ok + #+BEGIN_SRC sh +make TARGET=sky motelist + #+END_SRC + +* Compile and upload Hello World +1. To access the device, you'll need to have =dialout= access + #+BEGIN_SRC sh +$ id +uid=1000(vagrant) gid=1000(vagrant) groups=1000(vagrant),20(dialout),998(docker) + #+END_SRC +2. For each =make= command, you can specify + 1. =TARGET= (will be either =sky= or =native=) + 2. =BOARD= (only needed for some boards) + 3. =PORT= (e.g., =/dev/ttyUSB0=) +3. Save typing by saving the =TARGET= (gets saved just for that directory) + #+BEGIN_SRC sh +make TARGET=sky savetarget + #+END_SRC + You can also set the =PORT= (for that login session) using: + #+BEGIN_SRC sh +export PORT=/dev/ttyUSB0 + #+END_SRC + +4. Compile your code + #+BEGIN_SRC sh +$ make hello-world + #+END_SRC +5. Upload your code to the device (you may need to tell it the =PORT=): + #+BEGIN_SRC sh +$ make hello-world.upload + #+END_SRC +6. To see the output from the program, connect to the serial port using: + #+BEGIN_SRC sh +$ make login + #+END_SRC +7. If you get some garbled output, try resetting the mote using the reset button. + +* Contiki-NG build system: quick look +See https://github.com/contiki-ng/contiki-ng/wiki/The-Contiki%E2%80%90NG-build-system + +The build system is comprised of multiple =Makefile= files + 1. =projectdir/Makefile= contains options and instructions for that project and an =include= for the master Contiki makefile. e.g., + #+BEGIN_SRC sh +CONTIKI_PROJECT = udp-client udp-server +all: $(CONTIKI_PROJECT) + +CONTIKI=../.. +include $(CONTIKI)/Makefile.include + #+END_SRC + 2. =Makefile.include= is in the root of the Contiki-NG source tree. It includes: + - C files needed for core system + - includes =Makefile.$TARGET= and =app= Makefiles + 3. =Makefile.$(TARGET)= where =$(TARGET)= is the platform (e.g., =sky=) + - located in =arch/platforms/$(TARGET)= + - includes C files that are specific to that platform (e.g., =sht11-sensor.c=) + 4. =Makefile.$(CPU)= where =$(CPU)= is the processor (e.g., =msp430=) + - located in =arch/cpu/$(CPU)= + - includes code specific to that processor (e.g., =flash.c=) + 5. =Makefile.$(MODULE)= where =$(MODULE)= is the name of a module in the =os= directory. + + +* Things to try out +1. =contiki/examples= +2. =iot-workshop= branch in https://github.com/alignan/contiki + +* Useful resources +- Get started with Contiki-NG: https://www.contiki-ng.org/ +- IoT in five days: https://github.com/marcozennaro/IPv6-WSN-book/releases/ +- Contiki tutorial: https://www.slideshare.net/salahecom/contiki-seminar-1 +- Contiki tutorials: https://anrg.usc.edu/contiki/index.php/Contiki_tutorials + +* Thank you! + +* Additional info + +** Creating your own Contiki application +- Go to your home directory and create a subdirectory =my-first-app= + #+BEGIN_SRC sh +$ cd +$ mkdir my-first-app +#+END_SRC +- Inside the folder create a new C file =My-first-app.c= +- It may help to look at the examples to ensure you include all the right bits. +- Create a =Makefile= in the same folder. Ensure you specify the right directory + #+BEGIN_SRC makefile +CONTIKI_PROJECT = my-first-app +all: $(CONTIKI_PROJECT) + +CONTIKI=~/contiki-ng +include $(CONTIKI)/Makefile.include +#+END_SRC +- You should now be able to compile and upload the program +#+BEGIN_SRC sh +$ make TARGET=sky savetarget +$ make my-first-app +$ make my-first-app.upload +$ make login +#+END_SRC + +** Generic Structure of a Contiki Program +#+BEGIN_SRC c +/* header files */ +#include <...> + +/* declare processes */ +PROCESS(name, "description of process"); +/* ... */ +/* autostart processes */ +AUTOSTART_PROCESSES(name); +/* ... */ +/* define process */ +PROCESS_THREAD(name, ev, data) +{ + /* declare and static init vars */ + PROCESS_BEGIN(); + /* main code */ + PROCESS_END(); +} +#+END_SRC + + +** Contiki APIs: Process +#+attr_latex: :align L{0.4} L{0.6} +| Function | Description | +|-----------------------------+------------------------------------------------------------------------| +| =PROCESS_BEGIN()= | Define the beginning of a process | +| =PROCESS_END()= | Define the end of a process | +| =PROCESS_WAIT_EVENT()= | Wait for an event to be posted to the process | +| | Blocks the currently running process until an event is received | +| =PROCESS_WAIT_EVENT_UNTIL(c)= | Wait for an event to be posted to the process, with an extra condition | +| =PROCESS_YIELD()= | Yield the currently running process | +| =PROCESS_YIELD_UNTIL(c)= | Yield the currently running process until a condition occurs | +| =PROCESS_WAIT_UNTIL(c)= | Wait for a condition to occur | +| =PROCESS_EXIT()= | Exit the currently running process | +| =PROCESS_PAUSE()= | Yield the process for a short while | + +** Contiki APIs: Sensor +Each sensor has a set of functions for controlling it and query it for its state. +Some sensors also generate events when sensor values change. +A sensor must be activated before it generates events or relevant values. +#+attr_latex: :align L{0.4} L{0.6} +| Function | Description | +|----------------------------+------------------------------------------------------------------| +| =SENSORS_ACTIVATE(sensor)= | Activate the sensor | +| =SENSORS_DEACTIVATE(sensor)= | Deactivate the sensor | +| =sensor.value(o)= | Query the sensor for its last value (e.g. button pressed or not) | +| =sensor_event= | Event sent when a sensor has changed | + +** Contiki APIs: LED +| Function | Description | +|-----------------+----------------| +| =leds_on()= | Turn LEDs on | +| =leds_off()= | Turn LEDs off | +| =leds_invert()= | Toggle LEDs | +| =leds_blink()= | Blink all LEDs | + diff --git a/05-cooja.org b/05-cooja.org new file mode 100644 index 0000000..e144fce --- /dev/null +++ b/05-cooja.org @@ -0,0 +1,73 @@ +#+title: 7062CEM Cooja +#+Author: James Brusey +#+Email: j.brusey@coventry.ac.uk +#+Options: num:nil toc:nil +#+REVEAL_INIT_OPTIONS: width:1200, height:1200, margin: 0.1, minScale:0.2, maxScale:2.5, transition:'cube', slideNumber:true +#+REVEAL_THEME: white +#+REVEAL_HLEVEL: 1 +#+REVEAL_HEAD_PREAMBLE: +#+latex_header: \usepackage[osf]{mathpazo} +#+latex_header: \usepackage{booktabs} + +* Learning outcomes +- Familiarity with Cooja basics +- Know how to run and save a simple simulation +* What is Cooja? +- Java-based Contiki network simulation +- Allows simulation of small and large networks +- Two types of motes can be simulated: + - =cooja= motes run native code + - =sky= motes emulate the hardware but simulation may be slower as a result +- You shouldn't need to adjust your code to run in simulation + +* Getting started with Cooja +** Prerequisites +- While compiling Contiki doesn't require a windowing display, Cooja /does/ +- Two options for this: + - use VNC + - install a desktop environment +- Try the instructions at https://github.com/contiki-ng/contiki-ng/wiki/Vagrant#provision-a-vm-with-a-desktop-environment + +- Adjust the Vagrantfile to uncomment =vb.gui = true= + #+BEGIN_SRC ruby + config.vm.provider "virtualbox" do |vb| + # Display the VirtualBox GUI when booting the machine + vb.gui = true + end + #+END_SRC + +- Run the bootstrap script for setting up X packages +#+BEGIN_SRC sh +$ ./contiki-ng/tools/vagrant/bootstrap-vbox-with-x.sh +#+END_SRC +- Reboot after the install +- I found that this added some virtualbox-guest packages which were then removed by vagrant's vbguest plugin +- If you don't end up with a graphical login prompt, don't worry---you can just run =startx= on the command line after logging in on the gui window. +- You may need to download the cooja submodules if you haven't already + #+BEGIN_SRC sh + $ cd ~/contiki-ng + $ git submodule update --init --recursive + #+END_SRC + +** Create a new simulation +Follow the tutorial listed here: https://github.com/contiki-ng/contiki-ng/wiki/Tutorial:-Running-Contiki%E2%80%90NG-in-Cooja + +If you encounter errors, try the fix suggested here: https://github.com/contiki-os/contiki/issues/2324 + +[[file:figures/cooja-new-sim.png]] + +** Simple UDP RPL simulation +[[https://tools.ietf.org/html/rfc6550][RPL]] is the Routing Protocol for Low power and Lossy Networks + +https://github.com/contiki-ng/contiki-ng/wiki/Tutorial:-running-a-RPL-network-in-Cooja + +[[file:figures/cooja-rpl-udp.png]] + +** Next steps + +- Try the other simulation tutorials out until you feel you have a good understanding about what's going on. +- Review the code for the client and server and identify where messages are being sent and received. + +* Summary +- We've covered getting the simulation gui up and running and running a basic simulation +- This tool should come in handy when you write your own code diff --git a/06-protothreads.org b/06-protothreads.org new file mode 100644 index 0000000..cf3b3bd --- /dev/null +++ b/06-protothreads.org @@ -0,0 +1,214 @@ +#+title: 7062CEM Protothreads +#+Author: James Brusey +#+Email: j.brusey@coventry.ac.uk +#+Options: num:nil toc:nil +#+REVEAL_INIT_OPTIONS: width:1200, height:1200, margin: 0.1, minScale:0.2, maxScale:2.5, transition:'cube', slideNumber:true +#+REVEAL_THEME: white +#+REVEAL_HLEVEL: 1 +#+REVEAL_HEAD_PREAMBLE: +#+latex_header: \usepackage[osf]{mathpazo} +#+latex_header: \usepackage{booktabs} +* Learning outcomes + +- Understand why we need protothreads + +- Understand how they work + +- Be able to diagnose basic protothread errors + +* Motivation + +- Consider any task that involves blocking: + - opening a file + - reading a file + - interacting with the i2c bus + - sending a message over the radio + +- Without multi-tasking, we need to loop around and wait for completion +- Modern operating systems have some form of `wait' instruction that `blocks' this task until a certain thing has happened. +- In the meantime, other tasks can be run by `context switching' + +* Wait, what? + +- To multitask, we need to be able to do several things: + + 1. save the context (program counter, registers) + 2. load a different context (same but for a different process) + 3. restart executing + +- To do it really well, we also want to protect process A from process B + - we need memory protection / virtual memory addressing + - we need supervisor mode to do special things like switch context + - not all CPUs have support for all this! + +* State machines + +- Consider a simple control problem: toggling a light +- When we press the button and the light is *on*, we turn it *off* +- When we press the button and the light is *off*, we turn it *on* + +* How do we /express/ this + + - First we enumerate all possible states as $q_1, q_2, \ldots$ + - In this case, we have 2 states (light on or off) + - Then we define the conditions for transitions between states + - The result can be drawn as a graph + +#+ATTR_LATEX: :width 0.3\textwidth +[[file:figures/fsm.png]] + +Alternatively to labelling the circles (states) with whether the light is on or off, we can label the arcs (transitions) with the action of turning on or off the light. + +#+ATTR_LATEX: :width 0.3\textwidth +[[file:figures/fsm2.png]] + +* Representing a state machine as a table + +The previous state machine can also be represented as a table with columns for +1. what /state/ we are in +2. what additional /condition/ causes a transition +3. what /new state/ to transition to +4. /actions/ to take when transitioning + +| state | condition | new state | actions | +|-------+-----------+-----------+------------------| +| $q_1$ | push | $q_2$ | =turn_on_light= | +| $q_2$ | push | $q_1$ | =turn_off_light= | +| | | | | + + +* Converting to code +#+BEGIN_SRC C +void toggle() { + while (1) { + while (! get_button()) {} + turn_on_light(); + while (! get_button()) {} + turn_off_light(); + } +} +#+END_SRC +- but this means we can't do anything else while waiting for the button press + +* Just one state transition + +- *Key idea*: perform one state transition per function call + +#+BEGIN_SRC C +int fsm(int state) { + button = get_button(); + if (state == 1 && button) { + turn_on_light(); + state = 2; + } + else if (state == 2 && button) { + turn_off_light(); + state = 1; + } + return state; +} +#+END_SRC + +* So what do protothreads do? + +[[http://dunkels.com/adam/pt/expansion.html]] + +Note: in Contiki, the macros start =PROCESS=, not =PT= + +* Exercise - Check out the macro expansion + +#+BEGIN_SRC sh +$ cd contiki/examples/hello-world +$ make TARGET=native hello-world.e +#+END_SRC +* Examine the result +- can you see what each macro got substituted for? +#+BEGIN_SRC C +static char process_thread_hello_world_process(struct pt *process_pt, process_event_t ev, process_data_t data) +{ + static struct etimer timer; + { char PT_YIELD_FLAG = 1; if (PT_YIELD_FLAG) {;} switch((process_pt)->lc) { case 0:; + etimer_set(&timer, 1000 * 3); + while(1) { + printf("Hello, James\n"); + do { PT_YIELD_FLAG = 0; (process_pt)->lc = 60; case 60:; if((PT_YIELD_FLAG == 0) || !(etimer_expired(&timer))) { return 1; } } while(0); + etimer_reset(&timer); + } + }; PT_YIELD_FLAG = 0; (process_pt)->lc = 0;; return 3; }; +} +#+END_SRC +* Communicating between two processes +- It seems like we can call one process from another +- However, you should never do this! + - Think about what =process_pt= struct you are passing in +- Instead use =PROCESS_POST= to queue an event that is then received by the other process + +* Understanding PAUSE versus YIELD +- As discussed [[https://github.com/contiki-ng/contiki-ng/wiki/Documentation:-Processes-and-events#pausing-and-yielding][in the wiki]], =PROCESS_PAUSE= is not the same as =PROCESS_YIELD= +- PAUSE expects to be called again as soon as possible +- YIELD says - wait for the next event (and the processor can sleep) +- This is why we typically want to use event timers and event waits, so that the processor can sleep while waiting +- A nice exercise to try here is to compare an ordinary =timer= with an =etimer= + - What sort of wait do we need for =timer=? + - Do both operate in the same way? + - Which one allows the processor to sleep? +** Using event timer +The normal approach to sleeping (or delaying) for some duration is to use an event timer. +#+BEGIN_SRC c + static struct etimer timer; + PROCESS_BEGIN (); + /* Setup a periodic timer that expires after 10 seconds. */ + etimer_set (&timer, 10 * CLOCK_SECOND); + while (1) + { + printf ("etimer reading is %lu\n", clock_time()); + /* Wait for the periodic timer to expire and then restart the timer. */ + PROCESS_WAIT_EVENT_UNTIL (etimer_expired (&timer)); + etimer_reset (&timer); + } + PROCESS_END (); +#+END_SRC +** Using a normal timer +It is possible to use a normal timer but notice that we need to use PAUSE to ensure that the process is still considered active. This will have the negative side effect of not allowing the processor to sleep. +#+BEGIN_SRC c + static struct timer timer; + PROCESS_BEGIN (); + /* Setup a periodic timer that expires after 10 seconds. */ + timer_set (&timer, 10 * CLOCK_SECOND); + while (1) + { + printf ("timer reading is %lu\n", clock_time()); + /* Wait for the periodic timer to expire and then restart the + timer. */ + do { + PROCESS_PAUSE(); + } while (!timer_expired(&timer)); + timer_reset (&timer); + } + PROCESS_END (); +#+END_SRC + +* Things to watch for +- =process_pt= is a structure with =lc= being the line counter + +- rather than loop and wait, set =lc= to the current line and return immediately + +- the =switch= and =case= causes a jump into the inside of the loop when =lc= is 60! + +* Key ideas + +- Protothreads are a super-lightweight way to get multiple processes to run concurrently. + +- PT use (tricky) macros to turn ordinary looking code into a state machine + +- Understanding how they work helps when diagnosing compilation problems + +* Summary + +- We've uncovered the heart of Contiki, which is concurrency through protothreads + +- Understanding PTs will help when trying to understand compiler errors +* Additional reading + +https://github.com/contiki-ng/contiki-ng/wiki/Documentation:-Multitasking-and-scheduling + diff --git a/07-timers.org b/07-timers.org new file mode 100644 index 0000000..3c0fedb --- /dev/null +++ b/07-timers.org @@ -0,0 +1,223 @@ +#+title: 7062CEM Timers +#+Author: James Brusey +#+Email: j.brusey@coventry.ac.uk +#+Options: num:nil toc:nil +#+REVEAL_INIT_OPTIONS: width:1200, height:1200, margin: 0.1, minScale:0.2, maxScale:2.5, transition:'cube', slideNumber:true +#+REVEAL_THEME: white +#+REVEAL_HLEVEL: 1 +#+REVEAL_HEAD_PREAMBLE: +#+latex_header: \usepackage[osf]{mathpazo} +#+latex_header: \usepackage{booktabs} +#+latex_header: \usepackage{array} +#+latex_header: \newcolumntype{L}[1]{>{\raggedright\arraybackslash}p{#1\columnwidth}} +#+latex_header: \newcolumntype{C}[1]{>{\centering\arraybackslash}p{#1\columnwidth}} +#+latex_header: \newcolumntype{R}[1]{>{\raggedleft\arraybackslash}p{#1\columnwidth}} + +The following material is based on the Contiki-NG wiki section on Timers + +* Learning objectives +- understand what a timer is +- understand different timer types +- know which timer to use for which type of situation + +* What are timers for? +- perform some operation at regular periods (e.g., flash the led) +- wake from a low-power state at a scheduled time +- accurately schedule radio transmissions. + +- All timers build on =clock=, which tracks system time. +* Timer types ++ =timer=: a simple timer, without built-in notification (caller must check if expired). Safe from interrupt. ++ =stimer=: same as =timer=, but in seconds and with significantly longer wrapping period. Safe from interrupt. ++ =etimer=: schedules events to Contiki-NG processes. Unsafe from interrupt. ++ =ctimer=: schedules calls to a callback function. Unsafe from interrupt. ++ =rtimer=: real-time task scheduling, with execution from ISR. Safe from interrupt. + +* The Clock Module + +| Function | Purpose | +|---------------------------------+----------------------------------------------| +| =clock_time_t clock_time()= | Get the system time in =clock_time_t= ticks. | +| =unsigned long clock_seconds()= | Get the system time in seconds. | +| =void clock_wait(int delay)= | Delay the CPU for a number of clock ticks. | +| =void clock_init(void)= | Initialize the clock module. | +| =CLOCK_SECOND= | The number of ticks per second. | + +=clock_time_t= is simply an unsigned integer. +It starts at zero but will eventually wrap around. + +* The Timer Library +A timer is declared as =struct timer= + +| Function | Purpose | +|----------------------------------------------------------+--------------------------------------------------| +| =void timer_set(struct timer *t, clock_time_t interval)= | Start the timer. | +| =void timer_reset(struct timer *t)= | Restart the timer from the previous expire time. | +| =void timer_restart(struct timer *t)= | Restart the timer from current time. | +| =int timer_expired(struct timer *t)= | Check if the timer has expired. | +| =clock_time_t timer_remaining(struct timer *t)= | Get the time until the timer expires. | + +A timer is always initialized by a call to =timer_set()= which sets the timer to expire the specified delay from current time and also stores the time interval. +All the other function operate on this delay. +The following example shows how a timer can be used to detect timeouts in an interrupt. + +#+begin_src C + #include "sys/timer.h" + + static struct timer rxtimer; + + void init(void) { + timer_set(&rxtimer, CLOCK_SECOND / 2); + } + + interrupt(UART1RX_VECTOR) + uart1_rx_interrupt(void) + { + if(timer_expired(&rxtimer)) { + /* Timeout */ + ... + } + timer_restart(&rxtimer); + ... + } +#+end_src + +* The Stimer Library +The Contiki-NG stimer library provides a timer mechanism similar to the timer library but uses time values in seconds, allowing much longer expiration times. The stimer library use =clock_seconds()= in the clock module to get the current system time in seconds. + +The API for the stimer library is shown below. It is similar to the timer library, but the difference is that times are specified as seconds instead of clock ticks. + +| Function | Purpose | +|-------------------------------------------------------------+---------------------------------------------------| +| =void stimer_set(struct stimer *t, unsigned long interval)= | Start the timer. | +| =void stimer_reset(struct stimer *t)= | Restart the stimer from the previous expire time. | +| =void stimer_restart(struct stimer *t)= | Restart the stimer from current time. | +| =int stimer_expired(struct stimer *t)= | Check if the stimer has expired. | +| =unsigned long stimer_remaining(struct stimer *t)= | Get the time until the timer expires. | + +The stimer library can safely be used from interrupts. + +* The Etimer Library +The Contiki-NG etimer library provides a timer mechanism that generate timed events. +An event timer will post the event =PROCESS_EVENT_TIMER= to the process that set the timer when the event timer expires. +The etimer library uses =clock_time= in the clock module to get the current system time. + +| Function | Purpose | +|------------------------------------------------------------+--------------------------------------------------------------| +| =void etimer_set(struct etimer *t, clock_time_t interval)= | Start the timer. | +| =void etimer_reset(struct etimer *t)= | Restart the timer from the previous expire time. | +| =void etimer_restart(struct etimer *t)= | Restart the timer from current time. | +| =void etimer_stop(struct etimer *t)= | Stop the timer. | +| =int etimer_expired(struct etimer *t)= | Check if the timer has expired. | +| =int etimer_pending()= | Check if there are any non-expired event timers. | +| =clock_time_t etimer_next_expiration_time()= | Get the next event timer expiration time. | +| =void etimer_request_poll()= | Inform the etimer library that the system clock has changed. | + +Note that the timer event is sent to the Contiki-NG process used to schedule the event timer. +If an event timer should be scheduled from a callback function or another process, =PROCESS_CONTEXT_BEGIN()= and =PROCESS_CONTEXT_END()= can be used to temporarily change the process context. + +The following example shows how an etimer can be used to schedule a process to run once per second. + +#+begin_src C + #include "sys/etimer.h" + + PROCESS_THREAD(example_process, ev, data) + { + static struct etimer et; + PROCESS_BEGIN(); + + /* Delay 1 second */ + etimer_set(&et, CLOCK_SECOND); + + while(1) { + PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&et)); + /* Reset the etimer to trig again in 1 second */ + etimer_reset(&et); + ... + } + PROCESS_END(); + } +#+end_src + +* The Ctimer Library +The Contiki-NG ctimer library provides a timer mechanism that calls a specified function when a callback timer expires. The ctimer library uses =clock_time()= in the clock module to get the current system time. + +| Function | Purpose | +|-------------------------------------------------------------------------------------------------+--------------------------------------------------| +| =void ctimer_set(struct ctimer *t, clock_time_t interval, void (*callback)(void *), void *ptr)= | Start the timer. | +| =void ctimer_reset(struct ctimer *t)= | Restart the timer from the previous expire time. | +| =void ctimer_restart(struct ctimer *t)= | Restart the timer from current time. | +| =void ctimer_stop(struct ctimer *t)= | Stop the timer. | +| =int ctimer_expired(struct ctimer *t)= | Check if the timer has expired. | + +This API is similar to the etimer library, with the main difference being that =ctimer_set()= takes a callback function pointer and a data pointer as arguments. When a ctimer expires, it will call the callback function with the data pointer as argument. + +The example below shows a how a ctimer can be used to schedule a callback to a function once per second. + +#+begin_src C + #include "sys/ctimer.h" + static struct ctimer timer; + + static void + callback(void *ptr) + { + ctimer_reset(&timer); + ... + } + + void + init(void) + { + ctimer_set(&timer, CLOCK_SECOND, callback, NULL); + } +#+end_src + +Note that although the callback timers are calling a specified callback function, the process context for the callback is set to the process used to schedule the ctimer. Do not assume any specific process context in the callback unless you are sure about how the callback timers are scheduled. + +** The Rtimer Library + :PROPERTIES: + :CUSTOM_ID: the-rtimer-library + :END: +The Contiki-NG rtimer library provides scheduling and execution of real-time tasks. The rtimer library uses its own clock module for scheduling to allow higher clock resolution. The macro =RTIMER_NOW()= is used to get the current system time in ticks and =RTIMER_SECOND= specifies the number of ticks per second. + +Unlike the other timer libraries in Contiki-NG, the real-time tasks pre-empt normal execution for the task to execute immediately. This sets some constraints for what can be done in real-time tasks because most functions do not handle preemption. Interrupt-safe functions such as =process_poll()= are always safe to use in real-time tasks but anything that might conflict with normal execution must be synchronized. + +*Contiki-NG currently supports only one active rtimer.* Among other things it means that if you use system functionality that has its own rtimer (for example, the TSCH stack), you will not be able to have rtimers at the application level. + +| Function | Purpose | +|------------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------| +| =void rtimer_set(struct rtimer *task, timer_clock_t time, rtimer_clock_t duration, rtimer_callback_t func, void *ptr)= | Setup a real-time task. | +| =RTIMER_NOW()= | Get the current time. | +| =RTIMER_CLOCK_LT(t0,t1)= | Check if the time =t0= is less than the time =t1=. | +| =RTIMER_SECOND= | The number of ticks per second. | +| =void rtimer_init(void)= | Initialize the rtimer library. | +| =void rtimer_run_next(void)= | Called by the rtimer scheduler to run next real-time task. | + +A real time task is always initialized by a call to the function =rtimer_set()=, which sets the delay (=time=) callback function pointer, and data pointer. The =duration= field is currently unused. The following example shows how a real-time task can be setup to execute four times per second. + +#+begin_src C + #include "sys/rtimer.h" + + static struct rtimer task; + + static void + callback(struct rtimer *t, void *ptr, int status) + { + if(rtimer_reschedule(&task, RTIMER_SECOND / 4, DURATION) != RTIMER_OK) { + /* Failed to reschedule timer. Recover by rescheduling from current time. */ + rtimer_schedule(&task, RTIMER_SECOND / 4, DURATION); + } + ... + } + + void + init(void) + { + rtimer_setup(&task, RTIMER_HARD, callback, NULL); + rtimer_schedule(&task, RTIMER_SECOND / 4, DURATION); + } +#+end_src + +* Summary +- Most often, we will use =etimer= to produce a simple delay in our code. +- Interrupts cannot safely use some types of timer since the memory area is shared with other processes. diff --git a/08-debugging.org b/08-debugging.org new file mode 100644 index 0000000..bf03dce --- /dev/null +++ b/08-debugging.org @@ -0,0 +1,263 @@ +#+title: 7062CEM Debugging +#+Author: James Brusey +#+Email: j.brusey@coventry.ac.uk +#+Options: num:nil toc:nil +#+REVEAL_INIT_OPTIONS: width:1200, height:1200, margin: 0.1, minScale:0.2, maxScale:2.5, transition:'cube', slideNumber:true +#+REVEAL_THEME: white +#+REVEAL_HLEVEL: 1 +#+REVEAL_HEAD_PREAMBLE: +#+latex_header: \usepackage[osf]{mathpazo} +#+latex_header: \usepackage{booktabs} +* Learning outcomes + +- Understand the basic tools for debugging + +- Understand the principles of unit testing + +- Resolving basic compile time problems + +* KISS + +#+BEGIN_QUOTE +... there are two ways of constructing a software design: One way is to make it so simple that there are /obviously/ no deficiencies, and the other way is to make it so complicated that there are no /obvious/ deficiencies. The first method is far more difficult. It demands the same skill, devotion, insight, and even inspiration as the discovery of the simple physical laws which underlie the complex phenomena of nature. + +--- C.A.R. Hoare, [[https://zoo.cs.yale.edu/classes/cs422/2011/bib/hoare81emperor.pdf][1980]] +#+END_QUOTE + +* Tools for debugging + +- LED testing (Toggle between ON and OFF) +- Using “printf” support +- Simulation +- Unit testing + +* Using LEDs + +- You will probably start to use LEDs for debugging when you transfer your code to the mote, especially if the mote is not tethered to a PC + +- Useful for answering questions like: + - Does some event or error state occur (ever)? + - Is an event occurring regularly (but not too fast)? + +- Not so good for: + - Is a sensor reading meaningful and changing with the environment? + - How many times is an event occurring (exactly)? + - Exactly which error state occurred? + +* Using printf + +- Good for: + - Checking sensor readings (is it reasonable?) + - Printing counts or numerical values + - Use =make login= to see the output + +- Not good for: + - Testing complex radio communication + - Testing a base station node connected serially + +* Simulation (Cooja) + +- Simulation is appropriate for + - larger scale tests (with more motes than available) + - network communication testing + +- Simulation might not help with: + - identifying simple problems (printf / leds may be quicker) + - sensor related issues + +* Unit testing + +- unit tests provide a way of testing an individual component or function without having to run the full application + +[[https://github.com/contiki-os/contiki/blob/master/apps/unit-test/example-test.c]] + +* Declarations +Start by including the =unit-test.h= header +#+BEGIN_SRC C +#include "contiki.h" +#include "unit-test.h" +#+END_SRC + +Each test method needs to be declared along with a descriptive string +#+BEGIN_SRC C +UNIT_TEST_REGISTER(arithmetic, "Arith ops"); +#+END_SRC + +* Unit test method +The method then begins like this: +#+BEGIN_SRC C +UNIT_TEST(arithmetic) +{ + /* declarations go here */ + + UNIT_TEST_BEGIN(); +#+END_SRC + +You then need to perform some sort of test that concludes with an assertion. +#+BEGIN_SRC C + UNIT_TEST_ASSERT(a + b == 3); +#+END_SRC + +Finally, the method should close with +#+BEGIN_SRC C + UNIT_TEST_END(); +} +#+END_SRC + +* Executing the unit test + +As with any Contiki program, you need to set up a process that actually runs the unit test. +#+BEGIN_SRC C +PROCESS(test_process, "Unit testing"); +AUTOSTART_PROCESSES(&test_process); + +PROCESS_THREAD(test_process, ev, data) +{ + PROCESS_BEGIN(); + + UNIT_TEST_RUN(arithmetic); + + PROCESS_END(); +} +#+END_SRC + +* Running the unit test + +Although unit tests can be run on a mote, it's easiest to run them +- natively (e.g., for testing a calculation or data-structure) or +- under simulation (e.g., for networking) +unless the test needs specific hardware that only the mote has. + +* Worked example +#+BEGIN_SRC sh +cd contiki-ng +mkdir examples/unit-test +cp examples/hello-world/Makefile examples/unit-test +cp os/services/unit-test/example-test.c examples/unit-test +#+END_SRC + +Then edit the Makefile to look more like this: +#+BEGIN_SRC +CONTIKI_PROJECT = example-test +all: $(CONTIKI_PROJECT) +MODULES += $(CONTIKI_NG_SERVICES_DIR)/unit-test + +CONTIKI = ../.. +include $(CONTIKI)/Makefile.include +#+END_SRC + +Add an autostart process declaration to example-test.c +#+BEGIN_SRC C +PROCESS(test_process, "Unit testing"); +AUTOSTART_PROCESSES(&test_process); /* <- */ +#+END_SRC + +The result will look something like this: +#+BEGIN_SRC +[WARN: Tun6 ] Failed to open tun device (you may be lacking permission). Running without network. +[INFO: Main ] Starting Contiki-NG-release/v4.6-54-g425587de4-dirty +[INFO: Main ] - Routing: RPL Lite +[INFO: Main ] - Net: tun6 +[INFO: Main ] - MAC: nullmac +[INFO: Main ] - 802.15.4 PANID: 0xabcd +[INFO: Main ] - 802.15.4 Default channel: 26 +[INFO: Main ] Node ID: 1800 +[INFO: Main ] Link-layer address: 0102.0304.0506.0708 +[INFO: Main ] Tentative link-local IPv6 address: fe80::302:304:506:708 +[INFO: Native ] Added global IPv6 address fd00::302:304:506:708 + +Unit test: Arith ops +Result: success +Exit point: example-test.c:58 +Start: 1927799 +End: 1927799 +Duration: 0 +Ticks per second: 1000 + +Unit test: String ops +Result: failure +Exit point: example-test.c:69 +Start: 1927799 +End: 1927799 +Duration: 0 +Ticks per second: 1000 +#+END_SRC + +* Unit test theory + +The unit testing approach may be one of the most important ideas in Computer Science. However, there's more to it than simply having a unit test. + +A Unit Test should: +- be written [[https://en.wikipedia.org/wiki/Test-driven_development][/prior/]] to writing the actual code +- produce no output when it succeeds + - You shouldn't have to wade through reams of output to work out if your test worked +- be simple and understandable +- achieve 100% /code coverage/ + - All statements in target unit get executed at least once during testing + +[[file:figures/TestingManifesto.jpg]] + +* Unit testing more complex code + +When unit testing code that interacts with a device or user, it is often necessary to write a testing /stub/ or /mock/ object that mimicks the device or user. + +- e.g., for our button press example, we can code a mock =get_button= method that checks the time and presses the button after 20 seconds and then after 60 seconds +- notice how it is natural to code this mock method as a state machine +#+BEGIN_SRC C +int get_button() { + static int state = 0; + static clock_time_t first_call_time; + + t = clock_time(); + if (state == 0) { + state = 1; + first_call_time = t; + return 0; + } + else + t -= first_call_time; + + if (state == 1 && t > 20 * CLOCK_SECOND) { + state = 2; + return 1; + } + else if (state == 2 && t > 40 * CLOCK_SECOND) { + state = 3; + return 1; + } + else + return 0; +} +#+END_SRC + +* Compile time problems + +1. First understand if the problem is during compilation (likely to be syntax) or linking (likely to be missing library or misspelled API call) +2. Consider asking your search engine +3. Check for hidden or special characters + #+BEGIN_SRC sh + cat -v myprog.c | diff - myprog.c + #+END_SRC +4. Make sure your indentation is correct + 1. Emacs will ordinarily keep your code indented properly + 1. If needed, use at the start of a line to check indent + 2. You can also mark a region and use the =M-x indent-region= command + 2. In sublime text, use the ~reindent~ option in the edit menu + +* Run time problems + +If your program compiles but still does not work as desired, consider: + +1. do you have unit tests? +2. is the code in your editor actually the code that is being executed? + - e.g., you may not have saved before compiling or you may be editing on a different machine +3. start with the test that maximises information returned +4. question your assumptions + - use printf statements to check them + - e.g., does a particular function actually get called? +5. as a last resort, use a debugging tool such as [[https://www.gnu.org/software/gdb/][GNU gdb]] +* Summary + +- Debugging can be the most challenging part of programming +- Unit testing is an important preventative approach +- Avoid single stepping through your code unless you really have to diff --git a/09-energy.org b/09-energy.org new file mode 100644 index 0000000..1f85d5f --- /dev/null +++ b/09-energy.org @@ -0,0 +1,169 @@ +#+title: 7062CEM Energy benchmarking +#+Author: James Brusey +#+Email: j.brusey@coventry.ac.uk +#+Options: num:nil toc:nil +#+REVEAL_INIT_OPTIONS: width:1200, height:1200, margin: 0.1, minScale:0.2, maxScale:2.5, transition:'cube', slideNumber:true +#+REVEAL_THEME: white +#+REVEAL_HLEVEL: 1 +#+REVEAL_HEAD_PREAMBLE: +#+latex_header: \usepackage[osf]{mathpazo} +#+latex_header: \usepackage{booktabs} +* Learning outcomes + +- Understand how to *measure* mote energy consumption + +- Be able to identifier energy consumers within a mote + +- Understand the *start-up*, *nominal*, and *long-term* aspects of energy consumption + +* Motivation + +- Deployment of wireless motes in a real environment often requires them to be /long-lived/ without intervention or need for battery change + +- To estimate lifetime, we need to measure power consumption (although battery choice may also be a factor) + +* Measurement + +** Basic theory + + Instantaneous power $P$ is a function of voltage $V$ and current $I$ + \[ P = VI \] + + Power varies over time and so the energy consumption is the integral of power + \[ E = \int_{0}^t P(t)\ \mathrm{d}t \] + + However we often only have a series of measurements and thus need to estimate based on a series of measurements + \[ E \approx \Delta t \sum_0^t P(t) \] + + where $\Delta t$ is the fixed time period between power measurements. +** Why is measurement difficult for motes? + +Wireless sensor devices have a mix of high current usage: +- sensing (particularly for some sensors) +- radio listening or transmission + +and low or medium current usage: +- sleep +- computation + +Furthermore, power consumption varies rapidly over time. + +It's often difficult to accurately characterise both types of usage with the one measurement device + +Sleep current is particularly difficult since it is both instantaneously small but a big overall contributor + +** Using an oscilloscope + +Oscilloscopes are good for accurately identifying time periods: + +- how long does sensing take? + +It is also possible to estimate the current draw by measuring the voltage drop for an in-series resistor + +Expect, however, a +/- 10% error on this measurement + +No good for measuring micro amps (e.g., sleep current) + +** Using a multimeter + +Precision multimeters are much better for measuring microamp currents + +However they tend to assume that current use is roughly constant + +Accuracy thus can be improved by +1. micro-benchmarking +2. using an R-C circuit to smooth input to the multimeter + + +** Using a power analyzer + +A power analyzer, such as the Qoitech OTII, simplifies the task of assessing power consumption and will simultaneously measure voltage and current. + +Power analyzers are more accurate than oscilloscopes for sleep current but a precision multimeter is the gold standard + +#+attr_latex: :placement [H] :width 0.5\textwidth +[[file:figures/qoitech-otii-arc-power-analyzer-dc-power-supply-data-logger.jpg]] +** Power analyzer output +#+attr_latex: :placement [H] :width 0.5\textwidth +file:figures/otii-analyze.jpg +** Microbenchmarking +Consider this power consumption graph: +#+attr_latex: :placement [H] :width 0.5\textwidth +[[file:figures/fig8-max-no-spikes.png]] + +We can identify 5 distinct time periods: +- CJC warm-up (this is for a thermocouple sensor) +- CPU active +- transmission +- polling +- idle + +** Microbenchmarking targeted measurement + +Microbenchmarking addresses the problem of characterising the power consumption of individual operational modes. + +For each operational mode (e.g., transmitting a radio packet): +1. Program the mote to repeatedly (X times) transmit a packet +2. Measure current (e.g., with a precision multimeter) +3. Measure time period (e.g., with an oscilloscope) and divide by X + +** Microbenchmarking summary + +We then form a table like this + +| Operation | Current (mA) | Time (s) | mAs | +|--------------+--------------+----------+-----| +| Sensing | | | | +| CPU active | | | | +| Radio send | | | | +| Radio listen | | | | +| Idle | | | | +|--------------+--------------+----------+-----| +| Total | | | --- | +| | | | | + +Note that to convert to power, we need to assume the voltage is constant (e.g., 3V) + +Here's a worked example from Klues et al. +#+attr_latex: :placement [H] :width 0.5\textwidth +[[file:figures/klues-table.png]] + +[[https://ieeexplore.ieee.org/abstract/document/7471452]] + +[[https://dl.acm.org/doi/pdf/10.1145/1294261.1294286?casa_token=TUXZOamkmJ8AAAAA:HAnvY8uNRzjntTuNuO34Lf9awk0dFLg3AQXWSSaHzAhsuavgZp7_xAFQK3FKbtOaXSYSFIz9qS6XS5I]] + + +* Consider the larger picture + +Not all behaviour will be in the short term. Consider the following graph of battery voltage state over time: +#+attr_latex: :placement [H] :width 0.5\textwidth +[[file:figures/fig12-owls-battery.png]] +The sudden downward dips in battery voltage reflect periods when the server was unavailable. During this period, wireless nodes were retrying each transmission repeatedly and thus using more energy. + +** Also consider start-up energy costs + +- Start-up energy consumption may be radically different and this will be an issue if you are using energy harvesting +#+attr_latex: :placement [H] :width 0.5\textwidth +[[file:figures/system-components.png]] + +* Contiki built-in tools + +[[https://github.com/contiki-ng/contiki-ng/wiki/Documentation:-Energest][Energest]] provides a simple way to estimate the energy use based on when the radio is being used + +Typical output looks like: +#+BEGIN_SRC +Energest: + CPU 0s LPM 9s DEEP LPM 0s Total time 10s + Radio LISTEN 10s TRANSMIT 0s OFF 0s + +Energest: + CPU 0s LPM 19s DEEP LPM 0s Total time 20s + Radio LISTEN 20s TRANSMIT 0s OFF 0s +#+END_SRC + + +* Summary + +- Microbenchmarking is a key idea for accurately measuring energy low-power motes + +- Don't just think about normal operation but also watch for exceptional behaviour occurring in the long term and during start-up diff --git a/10-sensing.org b/10-sensing.org new file mode 100644 index 0000000..d268a84 --- /dev/null +++ b/10-sensing.org @@ -0,0 +1,203 @@ +#+title: 7062CEM Sensors and sensing +#+Author: James Brusey +#+Email: j.brusey@coventry.ac.uk +#+Options: num:nil toc:nil +#+REVEAL_INIT_OPTIONS: width:1200, height:1200, margin: 0.1, minScale:0.2, maxScale:2.5, transition:'cube', slideNumber:true +#+REVEAL_THEME: white +#+REVEAL_HLEVEL: 1 +#+REVEAL_HEAD_PREAMBLE: +#+latex_header: \usepackage[osf]{mathpazo} +#+latex_header: \usepackage{booktabs} +* Learning outcomes + +Understand +- what a sensor is + +- what sensor deviation (or error) is + +- how to acquire sensor readings using the Telos Mote + +* What is a sensor? + +#+begin_quote +“A *sensor* is a *transducer* whose purpose is to *sense* (that is, to detect) some characteristic of its environs.” +--- wikipedia +#+end_quote + +A perfect sensor is only sensitive to the measured property and insensitive to other properties while not influencing the environment. + +* Sensor deviation + +A sensor measurement may be different from the true value due to systematic errors, e.g.: +- bias +- non-linearity +- drift +- hysteresis (or memory effects) + +or random errors, e.g.: +- digitization errors +- noise + +* How to reduce sensor deviation? + +Bias and non-linearity are generally lessened through *calibration* + +Noise is usually reduced by *filtering* +- e.g. moving average, Kalman filter, etc + +However, do not forget that some noise may be resolved by +- better sensor design (improved analogue circuitry) +- shielding cables from interference + +* Sensor types + +| temperature | +| humidity | +| gas concentration (e.g. CO2) | +| electricity (W or kWh) | +| switch or push button | +| pressure | +| acceleration | +| acoustic | +| hydrophone | +| fluid or gas flow rates | +| radiant heat | +| proximity (infrared) | +| magnetic (e.g. compass) | +| location (GPS) | +| gyroscopic | + +also see http://en.wikipedia.org/wiki/List_of_sensors + +* Telos Mote Hardware (front) +[[file:figures/tmote.jpg]] + +* Telos expansion connector +[[file:figures/telos-expansion.png]] + +* Obtaining sensor data in Contiki + +Driver files for the Telos mote are available under =arch/platform/sky/dev= + +To find a file starting with =sht11= use: +#+BEGIN_SRC sh +find . -name "sht11*" +#+END_SRC + +Each sensor needs to be declared as: +#+BEGIN_SRC C +SENSORS_SENSOR(sensor, NAME, value, configure, status); +#+END_SRC + +For example, the battery sensor for the Telos is in =arch/platform/sky/dev/battery_sensor.c= and in there you will see: +#+BEGIN_SRC C +SENSORS_SENSOR(battery_sensor, BATTERY_SENSOR, value, configure, status); +#+END_SRC + +** Things you can do to a sensor +The following C will activate, deactivate, and obtain the value from a sensor. +#+BEGIN_SRC C +SENSORS_ACTIVATE(sensor) +SENSORS_DEACTIVATE(sensor) +sensor.value(type); +#+END_SRC + +The type parameter depends on the sensor and refers to the type of reading you are requesting (e.g., temperature or humidity) + +** Battery example +#+BEGIN_SRC C +#include "dev/battery-sensor.h" +#include "stdio.h" +/*---------------------------------------------------------------------------*/ +PROCESS(test_battery_process, "Battery Sensor Test"); +AUTOSTART_PROCESSES(&test_battery_process); +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(test_battery_process, ev, data) +{ + static uint32_t battery; + PROCESS_BEGIN(); + SENSORS_ACTIVATE(battery_sensor); + + while(1) { + battery = battery_sensor.value(0); + battery *= 5000; + battery /= 4095; + printf("Battery: %u.%03uV\n", (uint16_t)battery / 1000, (uint16_t)battery % 1000); + } + PROCESS_END(); +} +#+END_SRC +Excerpt From: Antonio Liñán Colina, Alvaro Vives, Antoine Bagula, Marco Zennaro and Ermanno Pietrosemoli. “Internet of Things (IoT) in 5 days”. (added include for stdio.h) + +** Telos sensor example + +#+BEGIN_SRC C +#include "stdio.h" +#include "contiki.h" +#include "dev/sensor/sht11/sht11.h" +#include "dev/sensor/sht11/sht11-sensor.h" + +PROCESS(temp, "Temperature"); +AUTOSTART_PROCESSES(&temp); +PROCESS_THREAD(temp, ev, data) +{ + static struct etimer timer; + PROCESS_BEGIN(); + + /* Setup a periodic timer that expires after 10 seconds. */ + etimer_set(&timer, CLOCK_SECOND * 10); + SENSORS_ACTIVATE(sht11_sensor); + while (1) { + PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&timer)); + int temp = sht11_sensor.value(SHT11_SENSOR_TEMP); + printf("temp: %d\n", temp); + etimer_reset(&timer); + } + + PROCESS_END(); +} +#+END_SRC +*** Notes +1. The above code does not interpret the integer value returned. According to the data sheet, this value must be divided by 100 and 39.6 subtracted + \[ x/100-39.6 \] + In C, you need something like: + #+BEGIN_SRC C +float s = ((0.01*val) - 39.60); +int dec = s; +float frac = s - dec; + #+END_SRC +2. Other sensors can also be accessed by changing the parameter to =value= to =SHT11_SENSOR_?=. See the header files (in the =arch/dev/sensor/sht11= directory) for a complete list of options. + +* Proxy or virtual sensors +Some sensors can be used to measure something seemingly unrelated - think about what the following might measure +- light sensor inside cupboard +- humidity sensor in a bathroom +- CO2 sensor in bedroom (!) +- CO2 sensor in a car cabin +- temperature sensor in a pendant or worn device +- pressure sensor at the foot of a quay (underwater) + +* Key things to remember + +- /read/ the data sheet +- use sample code from Internet (warily!) +- be aware of conflicting use of pins +- check conversion + - what is the true maximum adc value? + - max / min voltage? +- deal with noise in hardware first (but software later) + - twist lead wires together + - reduce lead wire distance if possible + - filtering capacitor needed? placed where? + +* What we covered + +- what a sensor is +- what sort of sensors are on the Telos mote +- how to interface to them using Contiki code +- how to obtain detailed interfacing information for specific devices + +* Additional reading + +- datasheet for SHT11 (temperature and humidity sensor on Telosb) +- TelosB datasheet (see Aula) diff --git a/11-lab1.org b/11-lab1.org new file mode 100644 index 0000000..d714861 --- /dev/null +++ b/11-lab1.org @@ -0,0 +1,177 @@ +#+title: 7062CEM Lab Session 1 +#+Author: James Brusey +#+Email: j.brusey@coventry.ac.uk +#+Options: toc:nil +#+latex_header: \usepackage[osf]{mathpazo} +#+latex_header: \usepackage{booktabs} +* Prerequisites +- You must have access to a mote (you should have been allocated one) +* Learning outcomes +- Confirm understanding of the compilation process +- Be able to modify code, recompile it, load it to the mote, and check that it works + +* Connect to your mote +You will need to modify =Vagrantfile= to be able to connect to the mote. +#+BEGIN_SRC ruby + config.vm.provider "virtualbox" do |vb| + vb.customize ["modifyvm", :id, "--usb", "on", + "--usbxhci", "on", + "--paravirtprovider", "kvm"] + vb.customize ["usbfilter", "add", "0", + "--target", :id, + "--name", "MTM-CM5000MSP", + "--vendorid", "0x0403", + "--productid", "0x6001"] + # # Display the VirtualBox GUI when booting the machine + # vb.gui = true + # + # # Customize the amount of memory on the VM: + # vb.memory = "1024" + end +#+END_SRC + +Note the =vb.gui= line. You can uncomment to get a graphical user interface, which may be needed for Cooja. + +Also note that if you need to increase the available virtual memory, you can do this by uncommenting the =vb.memory= line and changing the value (it's in kilobytes). I often use 4096. + +To see if your device is connecting, from the =vagrant ssh= session, do this: +#+BEGIN_SRC sh +$ make TARGET=sky motelist +Port Serial VID PID Product Vendor +------------ -------- ------ ------ ------------- ------ +/dev/ttyUSB0 FTWGUH2B 0x0403 0x6001 MTM-CM5000MSP FTDI +#+END_SRC +* Blink the LED + +Start by finding and creating a Makefile for =blink.c= +#+BEGIN_SRC sh +$ cd contiki-ng +$ find . -name "blink.c" +#+END_SRC + +Your output should look like this: +#+BEGIN_SRC sh +./arch/platform/sky/apps/blink.c +#+END_SRC + +This tells you the directory to change into: +#+BEGIN_SRC sh +$ cd arch/platform/sky/apps +#+END_SRC + +You will then need a Makefile that looks something like this: +#+BEGIN_SRC makefile +CONTIKI_PROJECT = blink +all: $(CONTIKI_PROJECT) + +CONTIKI = ../../../.. +# note: check that above points to correct directory +include $(CONTIKI)/Makefile.include +#+END_SRC +Make sure that you have the correct number of =..= to match how deep you are in the directory tree. In this case, you need to go up 4 directory levels since you are 4 deep in the contiki-ng directory structure. + +** Edit the code + +The default code references =LEDS_ALL= as the LED to turn on but this doesn't seem to work with the Telos. Try other options here. For example, try =LEDS_RED=. + +The definitions of the leds can be found in =os/dev/leds.h= + +** Compile and download the code + +From the command line (you will need to adjust the USB port) +#+BEGIN_SRC sh +$ make TARGET=sky savetarget +$ make blink +$ make MOTES=/dev/ttyUSB0 blink.upload +#+END_SRC +Check the output carefully to ensure that the upload succeeded. + +# ** Check the camera + +# Login to the camera at http://cogentee.hopto.org:88 using the supplied password. + +# Does it blink? + +# Note that if other students are also working at the same time then it may be hard to tell which mote is yours. One way to tell is to watch for the busy green and red LEDs during programming. + +** Things to try + +Can you change the colour? + +Can you make two LEDs come on at once? + +* Use two processes to blink two LEDs at different rates + +We are now going to try to add an additional process to blink a different LED at a rate of once every 2 seconds. + +Use the following procedure: +1. Create a copy of your code + #+BEGIN_SRC sh +$ cp blink.c blinktwice.c + #+END_SRC +2. Edit the first line of your =Makefile= to include =blinktwice= +3. Edit =blinktwice.c= and make the following changes: + 1. Add a new PROCESS statement + #+BEGIN_SRC C +PROCESS(another_process, "My other process"); + #+END_SRC + 2. Add this new process to the /existing/ AUTOSTART statement (note that you need use =&= to give it the /address/ of the process) + #+BEGIN_SRC C +AUTOSTART_PROCESSES(&blink_process, &another_process); + #+END_SRC + 3. Make an entire copy of the process function and call it =another_process= + #+BEGIN_SRC C +PROCESS_THREAD(blink_ ... + ... +} + #+END_SRC + 4. Adjust the =etimer_set= statements to use =2*CLOCK_SECOND= in =another_process= only. + 5. Change the color of the LED being turned on and off +4. Recompile and test on the mote + +Did it work? If not, carefully review what you've done and check that you have properly downloaded your new code to the mote. + +* Convert the temperature reading + +If you've made it here, that's great! + +For this stage, we are going to read the temperature and check that we can convert it to an actual reading. + +Drivers for the Telos mote can be found in the sky platform directory =arch/platform/sky/dev=. + +Use the following code: +#+BEGIN_SRC C +#include "stdio.h" +#include "contiki.h" +#include "dev/sensor/sht11/sht11.h" +#include "dev/sensor/sht11/sht11-sensor.h" + +PROCESS(temp, "Temperature"); + +AUTOSTART_PROCESSES(&temp); + +PROCESS_THREAD(temp, ev, data) +{ + static struct etimer timer; + PROCESS_BEGIN(); + + /* Setup a periodic timer that expires after 10 seconds. */ + etimer_set(&timer, CLOCK_SECOND * 10); + SENSORS_ACTIVATE(sht11_sensor); + while (1) { + PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&timer)); + int temp = sht11_sensor.value(SHT11_SENSOR_TEMP); + printf("temp: %d\n", temp); + etimer_reset(&timer); + } + + PROCESS_END(); +} +#+END_SRC + +** Exercise + +Perform these steps: +1. Compile and test the above program. Note that you will need to adjust your Makefile to add the new program as a possible target. Optionally, put the new program into a directory of its own. +2. You should note that the temperature readings are not in degrees Celsius. Check the lecture notes for the sensing lecture to get the conversion formula. +3. Adjust your code to use the conversion formula to output the converted temperature. Note that you should split your value into an integer and fractional part when using =printf= rather than trying to using ="%f"= in your format string. diff --git a/14-lab2.org b/14-lab2.org new file mode 100644 index 0000000..fc829ac --- /dev/null +++ b/14-lab2.org @@ -0,0 +1,219 @@ +#+title: 7062CEM Lab Session 2 +#+Author: Nandor Verba +#+Email: ad2833@coventry.ac.uk +#+Options: toc:nil +#+latex_header: \usepackage[osf]{mathpazo} +#+latex_header: \usepackage{booktabs} + +* Prerequisites +- You must be able to access the remote server and use an editor to edit files on the remote server +- You must have access to a mote (you should have been allocated one) +- You must have Cooja set up in either your local environment or on the VNC server provided + +* Learning outcomes +- Gain a better understanding of routing protocols in Contiki-NG. +- Be able to understand and modify the RPL UDP examples to suit your needs. +- Be able to set up a new Cooja environment with your newly defined nodes and run a simulation. +- Be able to compile the code onto physical motes and check that it's working. + +* Simple RPL network with 2 nodes + +In this exercise you will take two RPL UDP examples and modify them so that they have two distinct and simplified behaviours. +This will allow you to better understand what each of the =simple_udp= components do and how to edit them to suit your needs. + +Start by finding the two rpl-udp examples: + +#+BEGIN_SRC sh +examples/rpl_udp/udp_client.c +examples/rpl_udp/udp_server.c +#+END_SRC + +Create a copy of the =rpl_udp= folder so you can edit the file freely and still keep the originals. +Make sure you are in the =contiki-ng/examples= folder when you do this. + +#+BEGIN_SRC sh +cp -r rpl_udp my_rpl_app +#+END_SRC + +** Step 1 - Simplifying the code +Edit the newly copied files by removing anything except the raw transmission and reply features. + +You should get the below code for your client: + +#+BEGIN_SRC C +#include "contiki.h" +#include "net/routing/routing.h" +#include "random.h" +#include "net/netstack.h" +#include "net/ipv6/simple-udp.h" + +#include "sys/log.h" +#define LOG_MODULE "App" +#define LOG_LEVEL LOG_LEVEL_INFO + +#define UDP_CLIENT_PORT 8765 +#define UDP_SERVER_PORT 5678 + +#define SEND_INTERVAL (10 * CLOCK_SECOND) + +static struct simple_udp_connection udp_conn; + +PROCESS(udp_client_process, "UDP client"); +AUTOSTART_PROCESSES(&udp_client_process); + +PROCESS_THREAD(udp_client_process, ev, data) +{ + static struct etimer periodic_timer; + static char str[32]; + uip_ipaddr_t dest_ipaddr; + + PROCESS_BEGIN(); + + /* Initialize UDP connection */ + simple_udp_register(&udp_conn, UDP_CLIENT_PORT, NULL, + UDP_SERVER_PORT, NULL); + + etimer_set(&periodic_timer, SEND_INTERVAL); + + + while(1) { + PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&periodic_timer)); + + if(NETSTACK_ROUTING.node_is_reachable() && NETSTACK_ROUTING.get_root_ipaddr(&dest_ipaddr)) { + /* Send to DAG root */ + LOG_INFO("Sending request to "); + LOG_INFO_6ADDR(&dest_ipaddr); + LOG_INFO_("\n"); + snprintf(str, sizeof(str), "hello from client"); + simple_udp_sendto(&udp_conn, str, strlen(str), &dest_ipaddr); + } else { + LOG_INFO("Not reachable yet\n"); + } + + /* Send Event Timer */ + etimer_set(&periodic_timer, SEND_INTERVAL); + } + + PROCESS_END(); +} +#+END_SRC + +Use the following code for the server: + +#+BEGIN_SRC C +#include "contiki.h" +#include "net/routing/routing.h" +#include "net/netstack.h" +#include "net/ipv6/simple-udp.h" + +#include "sys/log.h" +#define LOG_MODULE "App" +#define LOG_LEVEL LOG_LEVEL_INFO + +#define UDP_CLIENT_PORT 8765 +#define UDP_SERVER_PORT 5678 + +static struct simple_udp_connection udp_conn; + +PROCESS(udp_server_process, "UDP server"); +AUTOSTART_PROCESSES(&udp_server_process); + +static void +udp_rx_callback(struct simple_udp_connection *c, + const uip_ipaddr_t *sender_addr, + uint16_t sender_port, + const uip_ipaddr_t *receiver_addr, + uint16_t receiver_port, + const uint8_t *data, + uint16_t datalen) +{ + LOG_INFO("Received request '%.*s' from ", datalen, (char *) data); + LOG_INFO_6ADDR(sender_addr); + LOG_INFO_("\n"); +} + +PROCESS_THREAD(udp_server_process, ev, data) +{ + PROCESS_BEGIN(); + + /* Initialize DAG root */ + NETSTACK_ROUTING.root_start(); + + /* Initialize UDP connection */ + simple_udp_register(&udp_conn, UDP_SERVER_PORT, NULL, + UDP_CLIENT_PORT, udp_rx_callback); + + PROCESS_END(); +} +#+END_SRC + +** Step 2 - Compiling the code to check for errors + +Once your code is edited, try compiling it with the make commands and correct any errors. + +#+BEGIN_SRC sh +make TARGET=sky udp_client +make TARGET=sky udp_server +#+END_SRC + +** Step 2 - Loading the code onto Cooja and testing it + +Once you are happy that your code compiles, create a Cooja simulation with two motes. +You should initially add one server and one client. + +After running the code you should see the output similar to the following. +Note that several "Not reachable yet" messages occur before messages from the client are transmitted to the server. + +[[file:figures/cooja-networking.png]] + + +* Group exercise + + This exercise will have you working in separate break-out rooms with each room setting up their own RPL network tree. + You each have access to a single mote so you will need to work together to set up the network and be able to send and receive messages. + To ensure that you do not conflict with other rooms, edit your code to use the client and server port numbers assigned to your room. + +** Step 1 - Change both port numbers + +#+BEGIN_SRC C +#define UDP_CLIENT_PORT xxxx # <- change +#define UDP_SERVER_PORT yyyy # <- change +#+END_SRC + +Assign one person to run the server - the others will run clients. + +Optionally, add a LED blink when you send (red) or receive (green). + +Hint: This can be done, by turning on a led when the message is sent, waiting a brief period of time and turning it off again. e.g., + +#+BEGIN_SRC C +leds_on(LEDS_RED); +etimer_set(&et, CLOCK_SECOND/10); +PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&et)); +leds_off(LEDS_RED); +#+END_SRC + + +** Step 2 - Download the code to the mote and run! + +To upload your code the your device: + +Build the project for the example file 'my_test.c' + +#+BEGIN_SRC sh +make TARGET=sky my_test +#+END_SRC + +Upload the compiled files to the device where x is your usb number. + +#+BEGIN_SRC sh +make my_test.upload PORT=/dev/ttyUSB[x] +#+END_SRC + +Log in to see the outputs. + +#+BEGIN_SRC sh +make login PORT=/dev/ttyUSB[x] +#+END_SRC + +Hopefully it works! Good luck! diff --git a/19-applications.org b/19-applications.org new file mode 100644 index 0000000..4243cbc --- /dev/null +++ b/19-applications.org @@ -0,0 +1,160 @@ +#+title: 7062CEM Applications +#+Author: James Brusey +#+Email: j.brusey@coventry.ac.uk +#+Options: num:nil toc:nil +#+REVEAL_INIT_OPTIONS: width:1200, height:1200, margin: 0.1, minScale:0.2, maxScale:2.5, transition:'cube', slideNumber:true +#+REVEAL_THEME: white +#+REVEAL_HLEVEL: 1 +#+REVEAL_HEAD_PREAMBLE: +#+latex_header: \usepackage[osf]{mathpazo} +#+latex_header: \usepackage{booktabs} +* Learning outcomes + + Understand + - the different parts that make up a typical IoT application + - ethical issues that may arise + - key constraints on what IoT can do + +* Parts of an IoT Application + +** The mote or node + +[[file:figures/tmote.jpg]] + + - Generally, all motes receive the same software + +** The border router + + - Also known as sink, gateway, or root node + - Generally consists of a mote connected to the USB port of a PC + - Can also be a Raspberry PI and mote + - Converts protocols between mote network (e.g., RPL or 6LowPAN) and ordinary IP network + - May require special software that is different from all other motes + +** Message Queue + + - The Message Queue (e.g., MQTT) provides a connector between the source (motes) and sink (back-end database) + - Publish / subscribe architecture is used + - Security may be an issue and consider configuring passwords early in the design + +** Web-server (CoAP) + + - CoAP is the Constrained Application Protocol (RFC7252) + - It can be thought of as a REST web server approach + - The CoAP server provides a HTTP style service and sits on a PC + - Motes make CoAP /requests/ using GET, PUT, POST, DELETE verbs + - The server responds with /responses/ + - See IoT in 5 days for more info. + +** Rule-based systems (Node-RED, IFTTT) + + - Systems like Node-RED can subscribe to messages from MQTT + - They can then transform and act on those messages + - Node-RED is good for wiring systems together + +* Ethical issues + +** Considering privacy + + - If you are performing an `experiment', then standard ethical tests apply + - participants must have informed consent + - they must be able to opt out at any time + - Information can often be derived indirectly: + - Bathroom humidity might indicate when shower is in use + - Bedroom CO2 sensors might identify sleep patterns or sexual activity + - Participants should be anonymised + +** Consider GDPR issues + + - Data protection provides guidance about what data should be stored and processed + - Reprocessing data for a different purpose than the originally stated purpose is not allowed + - Further reading is [[https://ico.org.uk/for-organisations/guide-to-data-protection/guide-to-the-general-data-protection-regulation-gdpr/][available]] + +** Who owns the data? + + - Data ownership is often overlooked + - Your contract may state that the data is owned by the company who employed you + - When considering ownership, keep in mind GDPR reprocessing issues + +** It shouldn't be up to the individual to decide + + - As a researcher or an employee, neither you nor your superior should decide what is ethical + - There must always be a higher, independent authority + - This might be a professional association, such as the IEEE, or a university ethics board + +* Constraints + +** Radio limitations + +- Maximal transmission / reception distances are a factor of: + - transmitter power + - receiver power / sensitivity + - radio frequency + - building materials for any structures + - interference + +- Overall, expect typical IoT devices to have a range of around 20m indoors and about 100m outdoors + +*** Transmission power +- IoT devices tend to operate with much less power than laptop WiFi and thus will have a smaller range +- Transmission power can be adjusted to help reduce power consumption + +*** Radio frequency + +- As with all electromagnetic waves, higher frequencies are blocked by solid objects whereas lower frequencies tend to pass through +- WiFi, BlueTooth, ZigBee and other IEEE 802.15.4 use the 2.4GHz ISM band + +*** Building materials and shape + +- Electrically conductive materials (water or metal) tend to absorb RF better than non-conductive materials (wood or air) +- In some cases, structures may help to extend the range slightly (e.g., in a long corridor) + +*** Interference + +- Many systems use the same ISM bands and interference is common +- Channel hopping approaches may help to bypass interference - particularly when operating near heavy machinery + +** Network lifetime + + - How long a network lasts for without changing the batteries is an important factor for many applications + + - Power consumption is an important consideration and has been discussed in another lecture. + + - Energy harvesting may allow indefinite extension of the network life + + - Specialist batteries can improve lifetime also + + - Software approaches are another avenue for improving lifetime + +*** Battery technology + + - Specialist batteries may help extend the life of a system + + - Main consideration for the battery is volume / weight and mAh rating + + - For energy harvesting consider lead-acid batteries (although these can be dangerous in some situations) + + - Rechargeable batteries will tend to have shorter lifetimes per charge cycle but last longer overall + +*** Energy harvesting + + - Solar panels are inexpensive and reliable but you must also have a battery that you can charge to continue to operate overnight + + - Wind is less reliable + + - Supercapacitors can be used to smooth out small fluctuations + +** Memory + + - Most motes have very limited memory and thus will restrict + - code size + - stored data + - It may be possible to store more data in non-volatile flash memory + - e.g., Telos mote has 1Mb flash + +** Time accuracy + + - Motes generally have inaccurate clocks + - Protocols such as TSCH (Time Synchronized Channel Hopping) may help + - Assume that your mote clock is several seconds out + diff --git a/20-cw-tips.org b/20-cw-tips.org new file mode 100644 index 0000000..2349889 --- /dev/null +++ b/20-cw-tips.org @@ -0,0 +1,34 @@ +#+title: Tips for the coursework +#+Author: James Brusey +#+Email: j.brusey@coventry.ac.uk +#+Options: toc:nil +#+latex_header: \usepackage[osf]{mathpazo} +#+latex_header: \usepackage{booktabs} + +* Writing robust C code +- Avoid the use of global variables, particularly as shared areas between two processes +- Choose meaningful variable and function names. +- Remove code that is not relevant. + Do *not* leave commented out code in your final version that you submit---there really is no excuse for doing this. +- Avoid [[https://en.wikipedia.org/wiki/Magic_number_(programming)][magic numbers]]. +- Avoid unnecessary [[https://en.wikipedia.org/wiki/Side_effect_(computer_science)][side effects]]. + Use instead =struct= to create a data structure to hold information that needs to be preserved between function calls. +- Never ignore warnings from the compiler. +- Do not repeat yourself - you shouldn't have duplicates of the same section of code in several parts of your code listing. + Similarly, do not make several copies of the code for different parts of the demo (i.e., don't have one for when testing on the network and a separate source code for testing the sensor). +- Make cohesive and coherent functions - the contents of a function (or process thread) should sit naturally with one another. + E.g., avoid having a function that both performs some computation and sends a message---such functions should be split into two separate parts. +- Every function that *can* be tested *should* be tested. + E.g., when calculating the humidity from the sensor reading, a separate function is needed to perform the calculation and then this should be tested to see that it does its job correctly. + + +* Video production +- It's recommended to use screen capture when showing things on the screen +- Do not show your source code - consider what you would show a boss rather than an academic +- Do not show the compilation unless there is a strong reason (e.g., an error message that you couldn't resolve) +- Make sure you answer these questions: + 1. Do the test cases pass? + 2. Can the mote print out meaningful values including floating point numbers? + 3. Do those values change when we do something to the sensor? + 4. Does the networking mechanism support multi-hop? + diff --git a/concurrency.org b/concurrency.org new file mode 100644 index 0000000..71dea19 --- /dev/null +++ b/concurrency.org @@ -0,0 +1,197 @@ +#+title: 7062CEM Concurrency +#+Author: James Brusey +#+Email: j.brusey@coventry.ac.uk +#+Options: num:nil toc:nil +#+REVEAL_INIT_OPTIONS: width:1200, height:1200, margin: 0.1, minScale:0.2, maxScale:2.5, transition:'cube', slideNumber:true +#+REVEAL_THEME: white +#+REVEAL_HLEVEL: 1 +#+REVEAL_HEAD_PREAMBLE: +#+latex_header: \usepackage[osf]{mathpazo} +#+latex_header: \usepackage{booktabs} +* Learning outcomes +- understand concurrency +- understand Contiki support for concurrency + + +* What is concurrency? +With a uniprocessor (single CPU), concurrency occurs when either: +the operating system pre-empts one task to run another +an hardware interrupt occurs (and runs an interrupt driver) +For multiprocessors (most modern CPUs are), concurrency occurs with or without the above two things. +In essence concurrency means two (or more) things happening at the same time + + +concurrency with TinyOS? +yes - there are still hardware interrupts (e.g. the radio, low-level timers, etc) +also - multiple independent motes are concurrent + +example: housemates and beer* +Thread 1 (person 1): +look in fridge +if no beer, +go and buy beer +put beer in fridge +*Watson (2013) http://www.cl.cam.ac.uk/teaching/1415/ConcDisSys/2013a-ConcurrentSystems-1B-L1.pdf +Thread 2 (person 2): +look in fridge +if no beer, +go and buy beer +put beer in fridge +as with most concurrent systems, it works fine most of the time +BUT if either person looks while the other is at the shop buying beer … we end up with too much beer + +beer problem: solution 1 (leave note) +Thread 1 (person 1): +look in fridge +if no beer, & no note +write a note +go and buy beer +put beer in fridge +remove note +Thread 2 (person 2): +look in fridge +if no beer, & no note +write a note +go and buy beer +put beer in fridge +remove note +works for humans (mostly) +will it work for computers? +what is the potential problem? + +as code ... +# Thread 1 +(beer, note) = check_fridge() +if not beer: + if not note: + set_note(True) + buy_beer() + set_note(False) +# Thread 2 +(beer, note) = check_fridge() +if not beer: + if not note: + set_note(True) + buy_beer() + set_note(False) +how could these be interleaved (scheduled) so as to cause a problem? + +possible problem schedule +# Thread 1 +(beer, note) = check_fridge() +if not beer: + if not note: + + + + + + set_note(True) + buy_beer() + set_note(False) +# Thread 2 + + +(beer, note) = check_fridge() +if not beer: + if not note: + set_note(True) + buy_beer() + set_note(False) +This type of problem is called a race condition + +is this a problem? (yes) +getting a particular interleaving that causes a problem may be quite hard - making it unlikely that the problem shows up +however, “mostly correct” code is worse than “mostly wrong” +easier to identify and debug programs that fail all the time +no easy way to bring on the problem for rare failures +problem may even disappear when including debugging code or printfs + + +possible problem schedule +# Thread 1 +(beer, note) = check_fridge() +if not beer: + if not note: + + + + + + set_note(True) + buy_beer() + set_note(False) +# Thread 2 + + +(beer, note) = check_fridge() +if not beer: + if not note: + set_note(True) + buy_beer() + set_note(False) +the problem is that we read the “state” here +but don’t update it until here + +critical section +the section where we read “note” and then update it should not be interrupted - this is referred to as a critical section + +In TinyOS, a statement or block can be made uninterruptable by using the atomic keyword. e.g. +atomic { + t = note; + if (!t) note = TRUE; +} +if (!t) { buyBeer();... +but there is still a problem! do we still need beer? + +beer solution 2 +# Thread 1 +atomic { +(beer, note) = check_fridge() +if not beer: + if not note: + set_note(True) + buy_beer() + set_note(False) +} +# Thread 2 +atomic { +(beer, note) = check_fridge() +if not beer: + if not note: + set_note(True) + buy_beer() + set_note(False) +} +this has some problems though: +brute force (stops everything else from running) +may block timer or other interrupts + +tinyos solution +this will be left as an exercise but key ideas are: +use a boolean value to “lock” a set of variables or data structure +always use atomic when reading or writing the lock +never read or write to protected data structure unless holding the lock +if you can’t currently get the lock, retry later by “posting” a task +note: this means that you must be in a task to do the job. + +tinyos tools - async +Ordinary tinyos code is synchronous and thus executed to completion before any other synchronous code is run + +Interrupt drivers may be asynchronous in that they may be called before synchronous code has completed + +This is marked by “async” keyword on function definition + + + +exercise +examine some async methods and identify why they are needing to be asynchronous + +what we’ve covered today +What is concurrency? +Housemates buying beer example +TinyOS tools for concurrency - atomic and async + +additional reading +TinyOS programming (book) - chapter 11 +Any good OS textbook about concurrency diff --git a/figures/Makefile b/figures/Makefile new file mode 100644 index 0000000..90c0f27 --- /dev/null +++ b/figures/Makefile @@ -0,0 +1,7 @@ +all: fsm.png fsm2.png + +%.png: %.pdf + convert -density 300 -resize 50% $^ $@ + +%.pdf: %.tex + latexmk -pdf $^ diff --git a/figures/TestingManifesto.jpg b/figures/TestingManifesto.jpg new file mode 100644 index 0000000..c002a68 Binary files /dev/null and b/figures/TestingManifesto.jpg differ diff --git a/figures/contiki-process.tex b/figures/contiki-process.tex new file mode 100644 index 0000000..13c2410 --- /dev/null +++ b/figures/contiki-process.tex @@ -0,0 +1,22 @@ +\documentclass{standalone} % What kind of document this is +\usepackage{tikz} % Import the tikz package +\usetikzlibrary{arrows.meta, positioning} +\tikzset{node distance=2.5cm, + procstep/.style={ % Sets the properties for each step + rectangle, + rounded corners, + fill=gray!10}, + double distance=2pt, % Adjust appearance of accept states + every edge/.style={ % Sets the properties for each transition + draw, + ->,>=stealth % Makes edges directed with bold arrowheads + }} +\begin{document} +\begin{tikzpicture} + \node[procstep] (s1) {main entry point}; + \node[procstep, below=of s1] (s2) {initialisation} + edge [<-] (s2); + + +\end{tikzpicture} +\end{document} \ No newline at end of file diff --git a/figures/cooja-networking.png b/figures/cooja-networking.png new file mode 100644 index 0000000..ee8ef1f Binary files /dev/null and b/figures/cooja-networking.png differ diff --git a/figures/cooja-new-sim.png b/figures/cooja-new-sim.png new file mode 100644 index 0000000..e2b7090 Binary files /dev/null and b/figures/cooja-new-sim.png differ diff --git a/figures/cooja-rpl-udp.png b/figures/cooja-rpl-udp.png new file mode 100644 index 0000000..1a49525 Binary files /dev/null and b/figures/cooja-rpl-udp.png differ diff --git a/figures/doom-emacs.png b/figures/doom-emacs.png new file mode 100644 index 0000000..477c142 Binary files /dev/null and b/figures/doom-emacs.png differ diff --git a/figures/emacs-spacemacs.png b/figures/emacs-spacemacs.png new file mode 100644 index 0000000..461ff11 Binary files /dev/null and b/figures/emacs-spacemacs.png differ diff --git a/figures/fig12-owls-battery.png b/figures/fig12-owls-battery.png new file mode 100644 index 0000000..93d63fd Binary files /dev/null and b/figures/fig12-owls-battery.png differ diff --git a/figures/fig8-max-no-spikes.png b/figures/fig8-max-no-spikes.png new file mode 100644 index 0000000..3114613 Binary files /dev/null and b/figures/fig8-max-no-spikes.png differ diff --git a/figures/fsm.png b/figures/fsm.png new file mode 100644 index 0000000..5b06350 Binary files /dev/null and b/figures/fsm.png differ diff --git a/figures/fsm.tex b/figures/fsm.tex new file mode 100644 index 0000000..b829080 --- /dev/null +++ b/figures/fsm.tex @@ -0,0 +1,25 @@ +\documentclass{standalone} % What kind of document this is +\usepackage{tikz} % Import the tikz package +\usetikzlibrary{automata, arrows.meta, positioning} +\tikzset{node distance=2.5cm, % Minimum distance between two nodes. Change if necessary. + every state/.style={ % Sets the properties for each state + semithick, + fill=gray!10}, + initial text={}, % No label on start arrow + double distance=2pt, % Adjust appearance of accept states + every edge/.style={ % Sets the properties for each transition + draw, + ->,>=stealth, % Makes edges directed with bold arrowheads + auto, + semithick}} +\let\epsilon\varepsilon +\begin{document} +\begin{tikzpicture} + \node[state with output, initial] (q1) {$q_1$ \nodepart{lower} {off}}; + \node[state with output, right of=q1] (q2) {$q_2$ \nodepart{lower} {on}}; + \draw (q1) edge[loop above] node {} (q1); + \draw (q2) edge[loop above] node {} (q2); + \draw (q2) edge[bend left] node {push} (q1); + \draw (q1) edge[bend left] node {push} (q2); +\end{tikzpicture} +\end{document} \ No newline at end of file diff --git a/figures/fsm2.png b/figures/fsm2.png new file mode 100644 index 0000000..fafcb89 Binary files /dev/null and b/figures/fsm2.png differ diff --git a/figures/fsm2.tex b/figures/fsm2.tex new file mode 100644 index 0000000..7708971 --- /dev/null +++ b/figures/fsm2.tex @@ -0,0 +1,25 @@ +\documentclass{standalone} % What kind of document this is +\usepackage{tikz} % Import the tikz package +\usetikzlibrary{automata, arrows.meta, positioning} +\tikzset{node distance=2.5cm, % Minimum distance between two nodes. Change if necessary. + every state/.style={ % Sets the properties for each state + semithick, + fill=gray!10}, + initial text={}, % No label on start arrow + double distance=2pt, % Adjust appearance of accept states + every edge/.style={ % Sets the properties for each transition + draw, + ->,>=stealth, % Makes edges directed with bold arrowheads + auto, + semithick}} +\let\epsilon\varepsilon +\begin{document} +\begin{tikzpicture} + \node[state without output, initial] (q1) {$q_1$}; + \node[state without output, right of=q1] (q2) {$q_2$}; + \draw (q1) edge[loop above] node {} (q1); + \draw (q2) edge[loop above] node {} (q2); + \draw (q2) edge[bend left] node {push / turn off} (q1); + \draw (q1) edge[bend left] node {push / turn on} (q2); +\end{tikzpicture} +\end{document} diff --git a/figures/klues-table.png b/figures/klues-table.png new file mode 100644 index 0000000..b8d1395 Binary files /dev/null and b/figures/klues-table.png differ diff --git a/figures/nano.png b/figures/nano.png new file mode 100644 index 0000000..e01558c Binary files /dev/null and b/figures/nano.png differ diff --git a/figures/otii-analyze.jpg b/figures/otii-analyze.jpg new file mode 100644 index 0000000..22becb3 Binary files /dev/null and b/figures/otii-analyze.jpg differ diff --git a/figures/qoitech-otii-arc-power-analyzer-dc-power-supply-data-logger.jpg b/figures/qoitech-otii-arc-power-analyzer-dc-power-supply-data-logger.jpg new file mode 100644 index 0000000..008b691 Binary files /dev/null and b/figures/qoitech-otii-arc-power-analyzer-dc-power-supply-data-logger.jpg differ diff --git a/figures/ssh_putty.png b/figures/ssh_putty.png new file mode 100644 index 0000000..939a3bb Binary files /dev/null and b/figures/ssh_putty.png differ diff --git a/figures/system-components.png b/figures/system-components.png new file mode 100644 index 0000000..d8e80ca Binary files /dev/null and b/figures/system-components.png differ diff --git a/figures/telos-expansion.png b/figures/telos-expansion.png new file mode 100644 index 0000000..32d5d40 Binary files /dev/null and b/figures/telos-expansion.png differ diff --git a/figures/tmote.jpg b/figures/tmote.jpg new file mode 100644 index 0000000..b476924 Binary files /dev/null and b/figures/tmote.jpg differ diff --git a/figures/tramp-modeline.png b/figures/tramp-modeline.png new file mode 100644 index 0000000..aaf604f Binary files /dev/null and b/figures/tramp-modeline.png differ diff --git a/figures/vnc-localhost.png b/figures/vnc-localhost.png new file mode 100644 index 0000000..e4a902b Binary files /dev/null and b/figures/vnc-localhost.png differ diff --git a/figures/vnc_viewer.png b/figures/vnc_viewer.png new file mode 100644 index 0000000..f414680 Binary files /dev/null and b/figures/vnc_viewer.png differ diff --git a/figures/zolertia-firefly.png b/figures/zolertia-firefly.png new file mode 100644 index 0000000..ffe4639 Binary files /dev/null and b/figures/zolertia-firefly.png differ