Compare commits

..
48 Commits
Author SHA1 Message Date
Lennart Heimbs 7a99fdc45c Final versions of camera-tools 2019-09-16 15:41:31 +02:00
heimbsle69869 949649c9a9 Kevins BVericht zu Latex 2019-09-13 13:46:39 +02:00
heimbsle69869 98bbdbeaad Merge changes 2019-09-13 10:42:30 +02:00
heimbsle69869 a464c34316 New revised version 2019-09-13 10:35:40 +02:00
heimbsle69869 8021b5b75b New revised Version 2019-09-13 10:34:15 +02:00
heimbsle69869 6c0acea84f Update 'bericht/lennart/references.bib'
Add RedMatic installation manual
2019-09-10 14:57:50 +00:00
heimbsle69869 6ba5c4cf3a Update 'bericht/lennart/main.tex'
Add/Begin with RedMatic section for pivccu
2019-09-10 14:57:00 +00:00
heimbsle69869 ab8f7b0214 Add homegear install, raspimatic, yahm section 2019-09-09 20:42:12 +02:00
heimbsle69869 1ba3cd74f5 Add tex stuff to gitignore 2019-09-06 14:09:44 +02:00
heimbsle69869 6c36722807 Remove .png and .jpg from gitignore 2019-09-06 12:27:37 +02:00
heimbsle69869 8fbd97ab73 Let .jpg and .png files through gitignore 2019-09-04 20:39:17 +02:00
heimbsle69869 15f46de0a3 Merge branch 'master' of https://git.efi.th-nuernberg.de/gitea/heimbsle69869/smarthome-presence-detect 2019-09-04 20:36:33 +02:00
heimbsle69869 f55d040567 Add Latex files for projectarbeit bericht 2019-09-04 20:36:22 +02:00
Lennart Heimbs efa03a17a1 change port in client 2019-08-05 12:32:42 +02:00
Lennart Heimbs 37c47bab66 change port in imagezmq 2019-08-05 12:08:31 +02:00
heimbsle69869 16c3efba13 minor changes 2019-08-05 10:17:40 +02:00
heimbsle69869 13549bb594 add timing to person detection 2019-08-05 10:15:09 +02:00
heimbsle69869 3d24fdf5d0 add docstring 2019-08-04 22:18:49 +02:00
heimbsle69869 98b9985b17 add remote video stream to people counter 2019-08-04 22:15:55 +02:00
heimbsle69869 91256c65fe Refactor video_presence to class 2019-08-04 22:15:28 +02:00
heimbsle69869 71a00d4642 add even more vscode stuf 2019-08-04 22:14:20 +02:00
heimbsle69869 f7ff9a74b5 add more vscode stuff 2019-08-04 22:13:40 +02:00
heimbsle69869 1e6a3f1b6b add vscode stuff to gitignore 2019-08-04 22:12:40 +02:00
heimbsle69869 41ef2c4cb6 zmq version for video file 2019-08-04 17:31:58 +02:00
Lennart Heimbs bac291dc7b add imagezmq 2019-08-04 17:19:14 +02:00
heimbsle69869 ebff3999bd move streaming to new folder + other method 2019-08-04 17:05:59 +02:00
heimbsle69869 c3fea65346 add cv2 video stream 2019-08-04 16:37:40 +02:00
Lennart Heimbs 559fe7f09d start work Video stream from pi to faster pc 2019-08-04 16:29:57 +02:00
heimbsle69869 d2aa68c5dd Add vidual-debugging to video_pres and video capabilities to couter 2019-08-04 15:41:02 +02:00
heimbsle69869 a26557345f add differing methods for opencv ver 3/4 2019-08-04 12:05:22 +02:00
heimbsle69869 4426205b0f add alternative detection method 2019-08-04 09:17:13 +02:00
heimbsle69869 870c27d0cb Merge branch 'master' of https://git.efi.th-nuernberg.de/gitea/heimbsle69869/smarthome-presence-detect 2019-08-04 09:16:19 +02:00
heimbsle69869 cc9304365a Add img/vid to gitignore; minor changes in video/image presence 2019-08-04 09:14:46 +02:00
heimbsle69869 1d84ca43ae Delete 'arduino/Doorsensor_Dohle/doorsensor/libraries/MySensors/tests/Arduino/sketches/serial_gw_no_transport_soft_signing_whitelisting_full_debug/serial_gw_no_transport_soft_signing_whitelisting_full_debug.ino' 2019-08-03 13:22:34 +00:00
heimbsle69869 f5d3c74645 Delete 'arduino/Doorsensor_Dohle/doorsensor/libraries/MySensors/tests/Arduino/sketches/serial_gw_no_transport_hard_signing_whitelisting_full_debug/serial_gw_no_transport_hard_signing_whitelisting_full_debug.ino' 2019-08-03 13:22:17 +00:00
heimbsle69869 157462915d Delete 'arduino/Doorsensor_Dohle/doorsensor/libraries/MySensors/tests/Arduino/sketches/hard_signing_no_whitelisting_full_debug/hard_signing_no_whitelisting_full_debug.ino' 2019-08-03 13:21:00 +00:00
heimbsle69869 93c203c86f Add venv to gitignore 2019-08-02 18:35:38 +02:00
heimbsle69869 82afc72e3b Merge branch 'master' of https://git.efi.th-nuernberg.de/gitea/heimbsle69869/smarthome-presence-detect 2019-08-02 18:34:34 +02:00
heimbsle69869 36bba050e7 Upload files to 'camera' 2019-08-01 20:06:41 +00:00
lheimbs fb7560d92e Add Count people 2019-08-01 10:24:44 +02:00
lheimbs a89e27abfc Fix video, rename image, add images to gitignore 2019-08-01 10:00:51 +02:00
lheimbs 57f8e55d2a Add another camera project and a video detection alg 2019-07-31 21:18:15 +02:00
lheimbs d900f956db Add person_detect to camera.py 2019-06-24 13:56:54 +02:00
lheimbs f09358a0ff Remove no longer required files 2019-06-24 13:45:03 +02:00
lheimbs b743a6a164 Add *.pyc to gitignore 2019-06-24 13:36:54 +02:00
lheimbs 50be58e0c8 Add scp and pic taking to cam 2019-06-24 13:36:07 +02:00
lheimbs fe71e3400a Add scp method for debugging and a detection method 2019-06-24 13:33:31 +02:00
Johannes Krug 8e04a74abc Code verändert, läuft noch nicht 2019-06-21 11:27:24 +02:00
98 changed files with 5689 additions and 135 deletions
+286
View File
@@ -1 +1,287 @@
*.orig
*.pyc
venv/
bericht/lennart/old
camera/venv camera/venv
camera/images
camera/videos
*.h264
*.mp4
.vscode/
camera/.vscode/
camera/.vscode/launch.json
#--- tex ---
ore latex/pdflatex auxiliary files:
*.aux
*.lof
*.log
*.lot
*.fls
*.out
*.toc
*.fmt
*.fot
*.cb
*.cb2
.*.lb
## Intermediate documents:
*.dvi
*.xdv
*-converted-to.*
# these rules might exclude image files for figures etc.
# *.ps
# *.eps
# *.pdf
## Generated if empty string is given at "Please type another file name for output:"
.pdf
## Bibliography auxiliary files (bibtex/biblatex/biber):
*.bbl
*.bcf
*.blg
*-blx.aux
*-blx.bib
*.run.xml
## Build tool auxiliary files:
*.fdb_latexmk
*.synctex
*.synctex(busy)
*.synctex.gz
*.synctex.gz(busy)
*.pdfsync
## Build tool directories for auxiliary files
# latexrun
latex.out/
## Auxiliary and intermediate files from other packages:
# algorithms
*.alg
*.loa
# achemso
acs-*.bib
# amsthm
*.thm
# beamer
*.nav
*.pre
*.snm
*.vrb
# changes
*.soc
# comment
*.cut
# cprotect
*.cpt
# elsarticle (documentclass of Elsevier journals)
*.spl
# endnotes
*.ent
# fixme
*.lox
# feynmf/feynmp
*.mf
*.mp
*.t[1-9]
*.t[1-9][0-9]
*.tfm
#(r)(e)ledmac/(r)(e)ledpar
*.end
*.?end
*.[1-9]
*.[1-9][0-9]
*.[1-9][0-9][0-9]
*.[1-9]R
*.[1-9][0-9]R
*.[1-9][0-9][0-9]R
*.eledsec[1-9]
*.eledsec[1-9]R
*.eledsec[1-9][0-9]
*.eledsec[1-9][0-9]R
*.eledsec[1-9][0-9][0-9]
*.eledsec[1-9][0-9][0-9]R
# glossaries
*.acn
*.acr
*.glg
*.glo
*.gls
*.glsdefs
*.lzo
*.lzs
# uncomment this for glossaries-extra (will ignore makeindex's style files!)
# *.ist
# gnuplottex
*-gnuplottex-*
# gregoriotex
*.gaux
*.gtex
# htlatex
*.4ct
*.4tc
*.idv
*.lg
*.trc
*.xref
# hyperref
*.brf
# knitr
*-concordance.tex
# TODO Comment the next line if you want to keep your tikz graphics files
*.tikz
*-tikzDictionary
# listings
*.lol
# luatexja-ruby
*.ltjruby
# makeidx
*.idx
*.ilg
*.ind
# minitoc
*.maf
*.mlf
*.mlt
*.mtc[0-9]*
*.slf[0-9]*
*.slt[0-9]*
*.stc[0-9]*
# minted
_minted*
*.pyg
# morewrites
*.mw
# nomencl
*.nlg
*.nlo
*.nls
# pax
*.pax
# pdfpcnotes
*.pdfpc
# sagetex
*.sagetex.sage
*.sagetex.py
*.sagetex.scmd
# scrwfile
*.wrt
# sympy
*.sout
*.sympy
sympy-plots-for-*.tex/
# pdfcomment
*.upa
*.upb
# pythontex
*.pytxcode
pythontex-files-*/
# tcolorbox
*.listing
# thmtools
*.loe
# TikZ & PGF
*.dpth
*.md5
*.auxlock
# todonotes
*.tdo
# vhistory
*.hst
*.ver
# easy-todo
*.lod
# xcolor
*.xcp
# xmpincl
*.xmpi
# xindy
*.xdy
# xypic precompiled matrices and outlines
*.xyc
*.xyd
# endfloat
*.ttt
*.fff
# Latexian
TSWLatexianTemp*
## Editors:
# WinEdt
*.bak
*.sav
# Texpad
.texpadtmp
# LyX
*.lyx~
# Kile
*.backup
# gummi
.*.swp
# KBibTeX
*~[0-9]*
# auto folder when using emacs and auctex
./auto/*
*.el
# expex forward references with \gathertags
*-tags.tex
# standalone packages
*.sta
# Makeindex log files
*.lpz
@@ -30,7 +30,7 @@ float T_max, T_min; // maximale bzw. minimale gemesse
float T_center; // Temperatur in der Bildschirmmitte float T_center; // Temperatur in der Bildschirmmitte
boolean isConnected();
// *************************************** // ***************************************
// **************** SETUP **************** // **************** SETUP ****************
@@ -1,37 +0,0 @@
/*
* The MySensors Arduino library handles the wireless radio link and protocol
* between your home built sensors/actuators and HA controller of choice.
* The sensors forms a self healing radio network with optional repeaters. Each
* repeater and gateway builds a routing tables in EEPROM which keeps track of the
* network topology allowing messages to be routed to nodes.
*
* Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
* Copyright (C) 2013-2018 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors
*
* Documentation: http://www.mysensors.org
* Support Forum: http://forum.mysensors.org
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
*******************************
*/
#include <stdint.h>
#include <pins_arduino.h>
#define MY_DEBUG
#define MY_DEBUG_VERBOSE_SIGNING
#define MY_RADIO_RF24
//#define MY_SIGNING_SOFT
#define MY_SIGNING_ATSHA204
//#define MY_SIGNING_NODE_WHITELISTING {{.nodeId = GATEWAY_ADDRESS,.serial = {0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01}}}
#define MY_SIGNING_REQUEST_SIGNATURES
#ifndef MY_SIGNING_SOFT_RANDOMSEED_PIN
#define MY_SIGNING_SOFT_RANDOMSEED_PIN 7
#endif
#ifndef MY_SIGNING_ATSHA204_PIN
#define MY_SIGNING_ATSHA204_PIN 17
#endif
#include <MySensors.h>
@@ -1,37 +0,0 @@
/*
* The MySensors Arduino library handles the wireless radio link and protocol
* between your home built sensors/actuators and HA controller of choice.
* The sensors forms a self healing radio network with optional repeaters. Each
* repeater and gateway builds a routing tables in EEPROM which keeps track of the
* network topology allowing messages to be routed to nodes.
*
* Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
* Copyright (C) 2013-2018 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors
*
* Documentation: http://www.mysensors.org
* Support Forum: http://forum.mysensors.org
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
*******************************
*/
#include <stdint.h>
#include <pins_arduino.h>
#define MY_DEBUG
#define MY_GATEWAY_SERIAL
#define MY_DEBUG_VERBOSE_SIGNING
//#define MY_SIGNING_SOFT
#define MY_SIGNING_ATSHA204
#define MY_SIGNING_NODE_WHITELISTING {{.nodeId = GATEWAY_ADDRESS,.serial = {0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01}}}
#define MY_SIGNING_REQUEST_SIGNATURES
#ifndef MY_SIGNING_SOFT_RANDOMSEED_PIN
#define MY_SIGNING_SOFT_RANDOMSEED_PIN 7
#endif
#ifndef MY_SIGNING_ATSHA204_PIN
#define MY_SIGNING_ATSHA204_PIN 17
#endif
#include <MySensors.h>
@@ -1,37 +0,0 @@
/*
* The MySensors Arduino library handles the wireless radio link and protocol
* between your home built sensors/actuators and HA controller of choice.
* The sensors forms a self healing radio network with optional repeaters. Each
* repeater and gateway builds a routing tables in EEPROM which keeps track of the
* network topology allowing messages to be routed to nodes.
*
* Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
* Copyright (C) 2013-2018 Sensnology AB
* Full contributor list: https://github.com/mysensors/MySensors/graphs/contributors
*
* Documentation: http://www.mysensors.org
* Support Forum: http://forum.mysensors.org
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
*******************************
*/
#include <stdint.h>
#include <pins_arduino.h>
#define MY_DEBUG
#define MY_GATEWAY_SERIAL
#define MY_DEBUG_VERBOSE_SIGNING
#define MY_SIGNING_SOFT
//#define MY_SIGNING_ATSHA204
#define MY_SIGNING_NODE_WHITELISTING {{.nodeId = GATEWAY_ADDRESS,.serial = {0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x02,0x01}}}
#define MY_SIGNING_REQUEST_SIGNATURES
#ifndef MY_SIGNING_SOFT_RANDOMSEED_PIN
#define MY_SIGNING_SOFT_RANDOMSEED_PIN 7
#endif
#ifndef MY_SIGNING_ATSHA204_PIN
#define MY_SIGNING_ATSHA204_PIN 17
#endif
#include <MySensors.h>
@@ -0,0 +1,313 @@
#include <Wire.h>
#include "MLX90640_API.h"
#include "MLX90640_I2C_Driver1.h"
#include "SPI.h"
//#include "Adafruit_GFX.h"
//#include "Adafruit_ILI9341.h"
// For the ESP-WROVER_KIT, these are the default.
/*#define TFT_CS 15
#define TFT_DC 2
#define TFT_MOSI 13
#define TFT_CLK 14
#define TFT_RST 26
#define TFT_MISO 12
#define TFT_LED 27*/
/*Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_MOSI, TFT_CLK, TFT_RST, TFT_MISO);*/
const byte MLX90640_address = 0x33; //Default 7-bit unshifted address of the MLX90640
#define TA_SHIFT 8 //Default shift for MLX90640 in open air
static float mlx90640To[768];
paramsMLX90640 mlx90640;
int xPos, yPos; // Abtastposition
int R_colour, G_colour, B_colour; // RGB-Farbwert
int i, j; // Zählvariable
float T_max, T_min; // maximale bzw. minimale gemessene Temperatur
float T_center; // Temperatur in der Bildschirmmitte
// ***************************************
// **************** SETUP ****************
// ***************************************
void setup()
{
Serial.begin(115200);
Wire.begin();
Wire.setClock(400000); //Increase I2C clock speed to 400kHz
while (!Serial); //Wait for user to open terminal
Serial.println("MLX90640 IR Array Example");
if (isConnected() == false)
{
Serial.println("MLX90640 not detected at default I2C address. Please check wiring. Freezing.");
while (1);
}
Serial.println("MLX90640 online!");
//Get device parameters - We only have to do this once
int status;
uint16_t eeMLX90640[832];
status = MLX90640_DumpEE(MLX90640_address, eeMLX90640);
if (status != 0)
Serial.println("Failed to load system parameters");
if (status == 0)
Serial.println("dumpee_okay");
status = MLX90640_ExtractParameters(eeMLX90640, &mlx90640);
if (status != 0)
{
Serial.println("Parameter extraction failed");
Serial.print(" status = ");
Serial.println(status);
}
//Once params are extracted, we can release eeMLX90640 array
MLX90640_I2CWrite(0x33, 0x800D, 6401); // writes the value 1901 (HEX) = 6401 (DEC) in the register at position 0x800D to enable reading out the temperatures!!!
// ===============================================================================================================================================================
//MLX90640_SetRefreshRate(MLX90640_address, 0x00); //Set rate to 0.25Hz effective - Works
//MLX90640_SetRefreshRate(MLX90640_address, 0x01); //Set rate to 0.5Hz effective - Works
//MLX90640_SetRefreshRate(MLX90640_address, 0x02); //Set rate to 1Hz effective - Works
//MLX90640_SetRefreshRate(MLX90640_address, 0x03); //Set rate to 2Hz effective - Works
MLX90640_SetRefreshRate(MLX90640_address, 0x04); //Set rate to 4Hz effective - Works
//MLX90640_SetRefreshRate(MLX90640_address, 0x05); //Set rate to 8Hz effective - Works at 800kHz
//MLX90640_SetRefreshRate(MLX90640_address, 0x06); //Set rate to 16Hz effective - Works at 800kHz
//MLX90640_SetRefreshRate(MLX90640_address, 0x07); //Set rate to 32Hz effective - fails
//pinMode(TFT_LED, OUTPUT);
//digitalWrite(TFT_LED, HIGH);
/*tft.begin();
tft.setRotation(1);
tft.fillScreen(ILI9341_BLACK);
tft.fillRect(0, 0, 319, 13, tft.color565(255, 0, 10));
tft.setCursor(100, 3);
tft.setTextSize(1);
tft.setTextColor(ILI9341_YELLOW, tft.color565(255, 0, 10));
tft.print("Thermographie - stoppi");
tft.drawLine(250, 210 - 0, 258, 210 - 0, tft.color565(255, 255, 255));
tft.drawLine(250, 210 - 30, 258, 210 - 30, tft.color565(255, 255, 255));
tft.drawLine(250, 210 - 60, 258, 210 - 60, tft.color565(255, 255, 255));
tft.drawLine(250, 210 - 90, 258, 210 - 90, tft.color565(255, 255, 255));
tft.drawLine(250, 210 - 120, 258, 210 - 120, tft.color565(255, 255, 255));
tft.drawLine(250, 210 - 150, 258, 210 - 150, tft.color565(255, 255, 255));
tft.drawLine(250, 210 - 180, 258, 210 - 180, tft.color565(255, 255, 255));
tft.setCursor(80, 220);
tft.setTextColor(ILI9341_WHITE, tft.color565(0, 0, 0));
tft.print("T+ = ");
// drawing the colour-scale
// ========================
for (i = 0; i < 181; i++)
{
//value = random(180);
getColour(i);
tft.drawLine(240, 210 - i, 250, 210 - i, tft.color565(R_colour, G_colour, B_colour));
}
*/
}
// **********************************
// ************** LOOP **************
// **********************************
void loop(){
for (byte x = 0 ; x < 2 ; x++) //Read both subpages
{
uint16_t mlx90640Frame[834];
int status = MLX90640_GetFrameData(MLX90640_address, mlx90640Frame);
if (status < 0)
{
Serial.print("GetFrame Error: ");
Serial.println(status);
}
float vdd = MLX90640_GetVdd(mlx90640Frame, &mlx90640);
float Ta = MLX90640_GetTa(mlx90640Frame, &mlx90640);
float tr = Ta - TA_SHIFT; //Reflected temperature based on the sensor ambient temperature
float emissivity = 0.95;
MLX90640_CalculateTo(mlx90640Frame, &mlx90640, emissivity, tr, mlx90640To);
}
// determine T_min and T_max and eliminate error pixels
// ====================================================
mlx90640To[1*32 + 21] = 0.5 * (mlx90640To[1*32 + 20] + mlx90640To[1*32 + 22]); // eliminate the error-pixels
mlx90640To[4*32 + 30] = 0.5 * (mlx90640To[4*32 + 29] + mlx90640To[4*32 + 31]); // eliminate the error-pixels
T_min = mlx90640To[0];
T_max = mlx90640To[0];
for (i = 1; i < 768; i++)
{
if((mlx90640To[i] > -41) && (mlx90640To[i] < 301))
{
if(mlx90640To[i] < T_min)
{
T_min = mlx90640To[i];
}
if(mlx90640To[i] > T_max)
{
T_max = mlx90640To[i];
}
}
else if(i > 0) // temperature out of range
{
mlx90640To[i] = mlx90640To[i-1];
}
else
{
mlx90640To[i] = mlx90640To[i+1];
}
}
// determine T_center
// ==================
T_center = mlx90640To[11* 32 + 15];
// drawing the picture
// ===================
for (i = 0 ; i < 24 ; i++)
{
for (j = 0; j < 32; j++)
{
mlx90640To[i*32 + j] = 180.0 * (mlx90640To[i*32 + j] - T_min) / (T_max - T_min);
getColour(mlx90640To[i*32 + j]);
// tft.fillRect(217 - j * 7, 35 + i * 7, 7, 7, tft.color565(R_colour, G_colour, B_colour));
}
}
/*
tft.drawLine(217 - 15*7 + 3.5 - 5, 11*7 + 35 + 3.5, 217 - 15*7 + 3.5 + 5, 11*7 + 35 + 3.5, tft.color565(255, 255, 255));
tft.drawLine(217 - 15*7 + 3.5, 11*7 + 35 + 3.5 - 5, 217 - 15*7 + 3.5, 11*7 + 35 + 3.5 + 5, tft.color565(255, 255, 255));
tft.fillRect(260, 25, 37, 10, tft.color565(0, 0, 0));
tft.fillRect(260, 205, 37, 10, tft.color565(0, 0, 0));
tft.fillRect(115, 220, 37, 10, tft.color565(0, 0, 0));
tft.setTextColor(ILI9341_WHITE, tft.color565(0, 0, 0));
tft.setCursor(265, 25);
tft.print(T_max, 1);
tft.setCursor(265, 205);
tft.print(T_min, 1);
tft.setCursor(120, 220);
tft.print(T_center, 1);
tft.setCursor(300, 25);
tft.print("C");
tft.setCursor(300, 205);
tft.print("C");
tft.setCursor(155, 220);
tft.print("C");
*/
delay(20);
}
// ===============================
// ===== determine the colour ====
// ===============================
void getColour(int j)
{
if (j >= 0 && j < 30)
{
R_colour = 0;
G_colour = 0;
B_colour = 20 + (120.0/30.0) * j;
}
if (j >= 30 && j < 60)
{
R_colour = (120.0 / 30) * (j - 30.0);
G_colour = 0;
B_colour = 140 - (60.0/30.0) * (j - 30.0);
}
if (j >= 60 && j < 90)
{
R_colour = 120 + (135.0/30.0) * (j - 60.0);
G_colour = 0;
B_colour = 80 - (70.0/30.0) * (j - 60.0);
}
if (j >= 90 && j < 120)
{
R_colour = 255;
G_colour = 0 + (60.0/30.0) * (j - 90.0);
B_colour = 10 - (10.0/30.0) * (j - 90.0);
}
if (j >= 120 && j < 150)
{
R_colour = 255;
G_colour = 60 + (175.0/30.0) * (j - 120.0);
B_colour = 0;
}
if (j >= 150 && j <= 180)
{
R_colour = 255;
G_colour = 235 + (20.0/30.0) * (j - 150.0);
B_colour = 0 + 255.0/30.0 * (j - 150.0);
}
}
//Returns true if the MLX90640 is detected on the I2C bus
boolean isConnected()
{
Wire.beginTransmission((uint8_t)MLX90640_address);
if (Wire.endTransmission() != 0)
return (false); //Sensor did not ACK
return (true);
}
@@ -0,0 +1,125 @@
/**
@copyright (C) 2017 Melexis N.V.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include <Arduino.h>
#include <Wire.h>
#include "MLX90640_I2C_Driver.h"
void MLX90640_I2CInit()
{
}
int MLX90640_I2CRead(uint8_t _deviceAddress, unsigned int startAddress, unsigned int nWordsRead, uint16_t *data)
{
// nWordsRead must be <= 32767
Wire.beginTransmission(_deviceAddress);
Wire.write(startAddress >> 8); //MSB
Wire.write(startAddress & 0xFF); //LSB
Wire.endTransmission(false);
i2c_err_t error = Wire.readTransmission(_deviceAddress, (uint8_t*) data, nWordsRead*2);
if(error != 0){//problems
Serial.printf("Block read from sensor(0x%02X) at address=%d of %d uint16_t's failed=%d(%s)\n",
_deviceAddress,startAddress,nWordsRead,error,Wire.getErrorText(error));
}
else { // reverse byte order, sensor Big Endian, ESP32 Little Endian
for(auto a = 0; a<nWordsRead; a++){
data[a] = ((data[a] & 0xff)<<8) | (( data[a]>>8)&0xff);
}
}
return 0;
}
/*
//Read a number of words from startAddress. Store into Data array.
//Returns 0 if successful, -1 if error
int MLX90640_I2CRead(uint8_t _deviceAddress, unsigned int startAddress, unsigned int nWordsRead, uint16_t *data)
{
//Caller passes number of 'unsigned ints to read', increase this to 'bytes to read'
uint16_t bytesRemaining = nWordsRead * 2;
//It doesn't look like sequential read works. Do we need to re-issue the address command each time?
uint16_t dataSpot = 0; //Start at beginning of array
//Setup a series of chunked I2C_BUFFER_LENGTH byte reads
while (bytesRemaining > 0)
{
Wire.beginTransmission(_deviceAddress);
Wire.write(startAddress >> 8); //MSB
Wire.write(startAddress & 0xFF); //LSB
if(Wire.endTransmission(false) != 7){
Serial.println("Error: Sensor did not ack");
return (0);//Sensor did not ACK
}
uint16_t numberOfBytesToRead = bytesRemaining;
if (numberOfBytesToRead > I2C_BUFFER_LENGTH) numberOfBytesToRead = I2C_BUFFER_LENGTH;
Wire.requestFrom((uint8_t)_deviceAddress, numberOfBytesToRead);
if (Wire.available())
{
for (uint16_t x = 0 ; x < numberOfBytesToRead / 2; x++)
{
//Store data into array
data[dataSpot] = Wire.read() << 8; //MSB
data[dataSpot] |= Wire.read(); //LSB
dataSpot++;
}
}
bytesRemaining -= numberOfBytesToRead;
startAddress += numberOfBytesToRead / 2;
}
return (0); //Success
}
*/
//Set I2C Freq, in kHz
//MLX90640_I2CFreqSet(1000) sets frequency to 1MHz
void MLX90640_I2CFreqSet(int freq)
{
//i2c.frequency(1000 * freq);
Wire.setClock((long)1000 * freq);
}
//Write two bytes to a two byte address
int MLX90640_I2CWrite(uint8_t _deviceAddress, unsigned int writeAddress, uint16_t data)
{
Wire.beginTransmission((uint8_t)_deviceAddress);
Wire.write(writeAddress >> 8); //MSB
Wire.write(writeAddress & 0xFF); //LSB
Wire.write(data >> 8); //MSB
Wire.write(data & 0xFF); //LSB
if (Wire.endTransmission() != 0)
{
//Sensor did not ACK
Serial.println("Error: Sensor did not ack");
return (0);//Sensor did not ACK
}
uint16_t dataCheck;
MLX90640_I2CRead(_deviceAddress, writeAddress, 1, &dataCheck);
if (dataCheck != data)
{
//Serial.println("The write request didn't stick");
return -2;
}
return (0); //Success
}
@@ -0,0 +1,51 @@
/**
@copyright (C) 2017 Melexis N.V.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#ifndef _MLX90640_I2C_Driver_H_
#define _MLX90640_I2C_Driver_H_
#include <stdint.h>
//Define the size of the I2C buffer based on the platform the user has
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
#if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__)
//I2C_BUFFER_LENGTH is defined in Wire.H
#define I2C_BUFFER_LENGTH BUFFER_LENGTH
#elif defined(__SAMD21G18A__)
//SAMD21 uses RingBuffer.h
#define I2C_BUFFER_LENGTH SERIAL_BUFFER_SIZE
#elif __MK20DX256__
//Teensy 3.2
#define I2C_BUFFER_LENGTH 32
#else
//The catch-all default is 32
#define I2C_BUFFER_LENGTH 32
#endif
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void MLX90640_I2CInit(void);
int MLX90640_I2CRead(uint8_t slaveAddr, unsigned int startAddress, unsigned int nWordsRead, uint16_t *data);
int MLX90640_I2CWrite(uint8_t slaveAddr, unsigned int writeAddress, uint16_t data);
void MLX90640_I2CFreqSet(int freq);
#endif
@@ -28,7 +28,6 @@
*/ */
#include <Wire.h> #include <Wire.h>
#include "MLX90640_API.h" #include "MLX90640_API.h"
#include "MLX90640_I2C_Driver.h" #include "MLX90640_I2C_Driver.h"
@@ -86,10 +85,10 @@ void loop()
float tr = Ta - TA_SHIFT; //Reflected temperature based on the sensor ambient temperature float tr = Ta - TA_SHIFT; //Reflected temperature based on the sensor ambient temperature
float emissivity = 0.95; float emissivity = 0.95;
Serial.print("loop-schleife");
MLX90640_CalculateTo(mlx90640Frame, &mlx90640, emissivity, tr, mlx90640To); MLX90640_CalculateTo(mlx90640Frame, &mlx90640, emissivity, tr, mlx90640To);
} }
Serial.print("loop-schleife2");
for (int x = 0 ; x < 10 ; x++) for (int x = 0 ; x < 10 ; x++)
{ {
Serial.print("Pixel "); Serial.print("Pixel ");
@@ -110,4 +109,5 @@ boolean isConnected()
if (Wire.endTransmission() != 0) if (Wire.endTransmission() != 0)
return (false); //Sensor did not ACK return (false); //Sensor did not ACK
return (true); return (true);
} Serial.print("verbunden");
}
@@ -23,7 +23,26 @@ void MLX90640_I2CInit()
{ {
} }
int MLX90640_I2CRead(uint8_t _deviceAddress, unsigned int startAddress, unsigned int nWordsRead, uint16_t *data)
{
// nWordsRead must be <= 32767
Wire.beginTransmission(_deviceAddress);
Wire.write(startAddress >> 8); //MSB
Wire.write(startAddress & 0xFF); //LSB
Wire.endTransmission(false);
i2c_err_t error = Wire.readTransmission(_deviceAddress, (uint8_t*) data, nWordsRead*2);
if(error != 0){//problems
Serial.printf("Block read from sensor(0x%02X) at address=%d of %d uint16_t's failed=%d(%s)\n",
_deviceAddress,startAddress,nWordsRead,error,Wire.getErrorText(error));
}
else { // reverse byte order, sensor Big Endian, ESP32 Little Endian
for(auto a = 0; a<nWordsRead; a++){
data[a] = ((data[a] & 0xff)<<8) | (( data[a]>>8)&0xff);
}
}
return 0;
}
/*
//Read a number of words from startAddress. Store into Data array. //Read a number of words from startAddress. Store into Data array.
//Returns 0 if successful, -1 if error //Returns 0 if successful, -1 if error
int MLX90640_I2CRead(uint8_t _deviceAddress, unsigned int startAddress, unsigned int nWordsRead, uint16_t *data) int MLX90640_I2CRead(uint8_t _deviceAddress, unsigned int startAddress, unsigned int nWordsRead, uint16_t *data)
@@ -42,7 +61,11 @@ int MLX90640_I2CRead(uint8_t _deviceAddress, unsigned int startAddress, unsigned
Wire.beginTransmission(_deviceAddress); Wire.beginTransmission(_deviceAddress);
Wire.write(startAddress >> 8); //MSB Wire.write(startAddress >> 8); //MSB
Wire.write(startAddress & 0xFF); //LSB Wire.write(startAddress & 0xFF); //LSB
Wire.endTransmission(false) if(Wire.endTransmission(false) != 7){
Serial.println("Error: Sensor did not ack");
return (0);//Sensor did not ACK
}
uint16_t numberOfBytesToRead = bytesRemaining; uint16_t numberOfBytesToRead = bytesRemaining;
if (numberOfBytesToRead > I2C_BUFFER_LENGTH) numberOfBytesToRead = I2C_BUFFER_LENGTH; if (numberOfBytesToRead > I2C_BUFFER_LENGTH) numberOfBytesToRead = I2C_BUFFER_LENGTH;
@@ -66,7 +89,7 @@ int MLX90640_I2CRead(uint8_t _deviceAddress, unsigned int startAddress, unsigned
return (0); //Success return (0); //Success
} }
*/
//Set I2C Freq, in kHz //Set I2C Freq, in kHz
//MLX90640_I2CFreqSet(1000) sets frequency to 1MHz //MLX90640_I2CFreqSet(1000) sets frequency to 1MHz
void MLX90640_I2CFreqSet(int freq) void MLX90640_I2CFreqSet(int freq)
@@ -87,7 +110,7 @@ int MLX90640_I2CWrite(uint8_t _deviceAddress, unsigned int writeAddress, uint16_
{ {
//Sensor did not ACK //Sensor did not ACK
Serial.println("Error: Sensor did not ack"); Serial.println("Error: Sensor did not ack");
return (-1); return (0);//Sensor did not ACK
} }
uint16_t dataCheck; uint16_t dataCheck;
Binary file not shown.
Binary file not shown.
Binary file not shown.
+127
View File
@@ -0,0 +1,127 @@
%%
%% LaTeX-Vorlage fuer Projektarbeit
%%
%% Neue User-Befehle
%%
%% \title Titel der Projektarbeit
%% \semester z.B. Wintersemester 2008/2009
%% \betreuer Kommagetrennte Liste der Betreuer
%% \maketitle Erzeugen der Titelseite
%% \keywords Schlüsselwörter angeben
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{arbeit}[2014/10/09 Paket fuer Projektarbeiten]
\RequirePackage{scrpage2}
\RequirePackage{hyperref} % Hyperref möglichst spät laden. Vielleicht hier?
% Kopf- und Fußzeilen setzen
\clearscrheadfoot
\pagestyle{scrheadings}
\cfoot{\thepage}
%% Schriftalternativen zu dem Latex-Standard: Computer Modern (CM)
%%----------------------------------------------------------------
%% Englaufende Times/Helvetica/Courier laden, aber mit richtigen Skalierungen (siehe l2tabu)
%\usepackage{mathptmx}
%\usepackage[scaled=.90]{helvet}
%\usepackage{courier} % kann auch weggelassen werden
%% Palatino, genauso wenig englaufend wie CM, aber riesiger Zeilenabstand
%\usepackage{mathpazo}
%\usepackage[scaled=.95]{helvet}
%\usepackage{courier}
%% Neue User-Befehle
%%------------------
\newcommand{\keywordsline}{\paragraph{Keywords:} \@keywords}
\newcommand{\@betreuer}{}
\newcommand{\betreuer}[1]{\renewcommand{\@betreuer}{#1}}
\newcommand{\@semester}{}
\newcommand{\semester}[1]{\renewcommand{\@semester}{#1}}
\newcommand{\@keywords}{}
\newcommand{\keywords}[1]{\renewcommand{\@keywords}{#1}}
\AtBeginDocument{
\hypersetup{bookmarksnumbered=true,
pdfcreator={Adobe Acrobat Distiller},
pdfproducer={LaTex with hyperref}
}
}
% Eigentliche Ausgabe der Titelseite
\renewcommand{\@maketitle}
{%
\hypersetup{%
pdftitle={\@title},
pdfsubject={Projektarbeit \@semester},
pdfauthor={\@author},
pdfkeywords={\@keywords},
}%
\begin{center}
\vskip 2em%
{\titlefont\huge \@title \par}%
\vskip 2em%
{\large\textbf{Projektarbeit}\par}%
{\large \@author\par}%
\vskip 2em%
{\large Fakultät Elektrotechnik Feinwerktechnik Informationstechnik, \\
Technische Hochschule Nürnberg Georg Simon Ohm,\\
Wassertorstraße 10,\\
90489 Nürnberg, Germany\\
Betreuer: \@betreuer\par}%
\vspace*{3em} %
\end{center}%
}
% Aus scrartcl.cls extrahiert, sorgt fuer richtiges Setzen bei ein-/zweispaltigem Satz
\renewcommand{\maketitle}{
\if@twocolumn
\ifnum \col@number=\@ne
\@maketitle
\else
\twocolumn[\@maketitle]%
\fi
\else
\newpage
\global\@topnum\z@
\@maketitle
\fi
}
% Aus scrartcl.cls genommen und angepasst -> Kursive Schrift
\renewenvironment{abstract}{%
\if@twocolumn\if@abstrt %Zweispaltig
\addsec*{\abstractname}
\it% Abstract kursiv setzen
\fi
\it% Abstract kursiv setzen
\else% Einspaltig
\if@abstrt
\small
\begin{center}
{\normalfont\sectfont\nobreak\abstractname
\vspace{-.5em}\vspace{\z@}}%
\end{center}
\fi
\quotation
\fi
}{%
\if@twocolumn\else\endquotation\fi
}
% Wir wollen Latex beim Setzen des Literaturverzeichnisses
% nicht ganz so pingelig sein lassen, da wir enge Spalten verwenden
\newcommand{\orig@bibliography}{}
\let\orig@bibliography\bibliography % Alte Definition in orig@... speichern
\renewcommand{\bibliography} % \bibliography neu definieren
{
\tolerance 1414 % Toleranzwerte setzen (siehe l2tabu)
\hbadness 1414 % ...
\emergencystretch 1.5em % ...
\hfuzz 0.3pt % ...
\widowpenalty=10000 % ...
\vfuzz \hfuzz % ...
\raggedbottom % ...
\orig@bibliography % und das Originalkommando aufrufen
}
+7
View File
@@ -0,0 +1,7 @@
% Hier werden die Kapitel eingebunden
% Bei Bedarf weitere Kapitel eintragen
\input{chapters/1_node-red_allgeimein.tex}
\input{chapters/2_flow.tex}
\input{chapters/3_subflows.tex}
\input{chapters/4_vergleich.tex}
\input{chapters/5_fazit.tex}
@@ -0,0 +1,110 @@
\section{Node-RED - Allgemein}
\label{kevin:node-red-allgemein}
Node-RED ist ein Werkzeug für die datenflussbasierte visuelle Programmierungmiteinerbrowser-basierte Entwicklungsumgebung.
Mit Node-RED ist es möglich verschiedene Geräte, APIs (Programmierschnittstellen)und Onlinedienste in einer grafischen Oberflächeauf einfache Weise virtuell zu verbinden.
Dadurch ist es unter der Verwendung der bereits vorinstallierten „Packages“ (Pakete mit bestimmten Funktionsweisen) Datenflüsse logisch miteinander zu verknüpfen.
Zusätzlichbesteht die Möglichkeit eigene Nodes in JavaScript zu programmieren oder mithilfe des bereits integrierten Palletten Manager benötigte Nodes nachträglich zu installieren.
Da Node-RED sehr leichtgewichtig ist, eignet es sich ideal für die Nutzung auf kostengünstiger Hardware wie dem Raspberry Pi.
\subsection{Node-RED Vorteile}
\label{kevin:node-red-vorteile}
\begin{itemize}
\item Einfache Handhabung via Drag and Drop
\item Ideal für die Implementierung von einfachen Abläufen
\item Einfach zu erweitern
\item Schnelle Gestaltung von interessanten Weboberflächen
\item plattformübergreifend, da Node.js als Runtime
\end{itemize}
\subsection{Node-RED in der Projektarbeit}
\label{kevin:node-red-in-projektarbeit}
\begin{figure}[H]
\centering
\includegraphics[width=.75\textwidth]{images/anwendungsschema-node-red.png}
\caption{Anwendungsschema von Node-RED in der Projektarbeit}
\label{fig:anwendungsschema-node-red}
\end{figure}
Das Tool Node-RED wird auf einem Raspberry Pi installiert, welcher als Webserver für die Erstellung des Dashboards dient.
In der Entwicklungsoberfläche werden die logischen Programmabläufe via „Drag and Drop" erstellt und Funktionsabläufe in der Programmiersprache JavaScript entwickelt.
Die Sensordaten werden von den Modulen (HomeMatic und eigene Sensoren) an den Raspberry weitergleitet und abschließend in Node-RED verarbeitet und visualisiert.
\subsection{Node-RED Installation/Inbetriebnahme}
\label{kevin:node-red-installation}
Die Installation von Node-RED wird in Abschnitt \ref{lenny:node-red-installation} auf Seite \pageref{lenny:node-red-installation} beschrieben.
\subsection{Browserbasierte Entwicklungsoberfläche}
\label{kevin:node-red-oberflaeche}
\begin{figure}[H]
\centering
\includegraphics[width=.75\textwidth]{images/ss-node-red.png}
\caption{Aufbau der Entwicklungsoberfläche in Node-RED}
\label{fig:ss-node-red}
\end{figure}
Nach der Installation ist die Node-RED Oberfläche im Browser unter folgender URL zu erreichen:
\begin{lstlisting}
http://ip-des-raspberry:1880
\end{lstlisting}
Die schlicht gehaltene Node-RED-Entwicklungsoberfläche lässt sich in drei Bereiche einteilen.
Im 1. Bereich (gelbe Markierung) ist die Node-Palette welche die mitgelieferten Nodes enthält.
Der 2. Bereich beschreibt den eigentlichen Arbeitsbereich, dieser Bereich wird Flow, Arbeitsblatt oder Workspace genannt.
Hier werden die logischen datenflussbasierten Abläufe und Funktionen mittels Drag \& Drop der Nodes aus der Node-Palette erstellt.
Durch das Plus-Zeichen in der oberen rechten Ecke im roten Bereich können mehrere Arbeitsblätter erstellt werden.
Im letzten 3. Bereich befindet sich das Informations-bzw. Debug-Panel.
Dort werden Info-Texte zu den einzelnen Nodes und Anleitungen für die Konfiguration angezeigt.
Der Debug-Tab zeigt die Ausgaben der Debug-Nodes und listet interne Fehler auf.
Durch die rote Schaltfläche „Implementieren“ oben rechts, werden alle Flows in den Raspberry Pi geladen und ausgeführt.
\subsection{Visualisierung inNode-RED}
\label{kevin:node-red-visualisierung}
In der Grundausstattung sind bereits einige nützliche Nodes installiert.
Werden für die Nutzung noch weitere Nodes benötigt, können diese direkt in der Web-Oberfläche nachinstalliert werden.
Hier kann nach neuen Nodes gesucht und diese per Mausklick installiert werden.
Für die Anzeige von Visualisierungen in dem Dashboard wird das „node-red-dashboard“ Package benötigt.
Für die Installation wird der Menüpunkt „Palette verwalten“ im Drop Down Menü oben rechts (3 horizontale Striche) ausgewählt.
Durch die Eingabe „dashboard“ in die Suchleiste, wird das notwendige Package angezeigt.
Mit der Bestätigung „installieren“ werden die benötigten Module in Node-RED hinzugefügt.
\begin{figure}[H]
\centering
\includegraphics[width=.75\textwidth]{images/pakete-verwalten-node-red.png}
\caption{Palette verwalten Dienst für nachträgliche Package-Installationen}
\label{fig:pakete-verwalten-node-red}
\end{figure}
\subsection{Node-RED Bedienoberfläche}
\label{kevin:node-red-bedienoberflaeche}
Aufgerufen wird die Web-Oberflächemit folgender URL:
\begin{lstlisting}
http://ip-des-raspberry:1880/ui
\end{lstlisting}
Der Aufbauder Gruppen innerhalb des User Interfaces kann über das Dashboard-Panel, welches als weiterer Tab im Info-/Debug-Panel angezeigt wird, vorgenommen werden.
In der Konfiguration können die Elemente auf mehrere Tabs und in Gruppen aufgeteiltwerden.
Gehören Elemente der gleichen Gruppe an, teilen sie sich eine Kachel in der Bedienoberfläche.
\begin{figure}[H]
\centering
\includegraphics[width=.75\textwidth]{images/ss-node-red-dashboard.png}
\caption{Bedienoberfläche im Webbrowser}
\label{fig:ss-node-red-dashboard}
\end{figure}
Für die Projektarbeit wurde das Dashboard in vier logisch zusammenhängende Gruppen eingeteilt.
Jede einzelne Gruppe repräsentiert eine Auswertung von Sensordaten mit visueller Darstellung.
Dabei wurde zwischen kommerziellen und den selbstgebauten Sensoren differenziert.
\paragraph{}
Durch die beiden Gruppen \emph{HomeMatic Präsenz \& Tür Sensor} und \emph{HomeMatic Powermeter} werden die kommerziellen Sensoren von HomeMatic vertreten, darunter ist der Präsenz Melder, die Steckdose und der Tür Sensor.
Die verschiedenen Sensoren von HomeMatic wurden von dem Kommilitonen Sebastian Dohle angelernt und angebracht.
Innerhalb der \emph{HomeMatic Powermeter} Gruppe werden die Sensordaten der Steckdose angezeigt, darunter auch ein grafischer Verlauf der Netzspannung und des Stroms in einer Time-Chart.
Bei \emph{HomeMatic Präsenz \& Tür Sensor} werden die Standard-Ausgabewerte der Sensorenangezeigt.
Darunter der aktuelle Status des Tür-Sensors, also ob offen oder zu und die aktuelle Präsenzmeldung.
\paragraph{}
Hinsichtlich der Gruppe \emph{Dashbaord} (links) sind die grafischen Auswertungen der eigenen Sensordaten dargestellt.
Hierbei ist in der oberen Hälfte der Ultraschall Sensor visualisiert.
Es werden nur die vereinfachten Werte 1 oder 0 angezeigt.
Das gleiche gilt für den PIR Sensor in der unteren Hälfte.
Die Sensordaten 1 oder 0 zeigen an ob der jeweilige Sensor ausgelöst hat oder nicht.
Rechts dazu in der \emph{Eigene Sensoren} Gruppe steht die textuelle Auswertung und eine logische Auswertung mit „Person(en) im Raum“ oder „Keine Person(en) im Raum“.
+73
View File
@@ -0,0 +1,73 @@
\section{Flow/Ausarbeit}
\label{kevin:flow}
In Node-RED gibt es die Möglichkeit Arbeitsblätter anzulegen.
Dadurch lassen sich die verschiedenen Sensoren in logische Gruppen verpacken.
Das Projekt bekommt dadurch eine bessere Übersicht und Strukturierung.
Jeder einzelner Flow beschreibt dabei eine eigene Logik, welche die Sensordaten auswertet und visualisiert.
Bei der datenflussbasierten Entwicklung in Node-RED wird der Datenaustausch durch sogenannte Message-Objekte umgesetzt.
\subsection{MainDashboard -Flow}
\label{kevin:maindashboard-flow}
\begin{figure}[H]
\centering
\includegraphics[width=.75\textwidth]{images/main-dashboard-flow.png}
\caption{Auswertung und Visualisierung des PIR-und Ultraschallsensors}
\label{fig:main-dashboard-flow}
\end{figure}
Für die visuelle Darstellung des PIR- und Ultraschallsensors ist das Flow „MainDashboard“ zuständig(s. Abb. \ref{fig:main-dashboard-flow}).
Zu Beginn sind im roten Bereich die beiden Subflows „UltraSchallSensor“ und „PirSensor“ erkennbar.
Diese repräsentieren dabei eine selbst entwickelte Logik und geben ihre Sensordaten weiter an das gelbe „merge“ Node im blauen Kasten.
Durch den „merge“ Baustein werden die beiden Datenströme miteinander kombiniert.
Dadurch können diese Datenstöme anschließend leichter durch den „PräsenzFunction“ Node ausgewertet werden.
Die beiden äußeren Zweige, haben eine einfache „TextFunction“ welche eine textuelle Darstellung der beiden Werte „Ausgelöst“ und „Nicht Ausgelöst“ in der UI anzeigen.
\subsection{HMPresenceSensor–Flow}
\label{kevin:hmpresencesensor-flow}
\begin{figure}[H]
\centering
\includegraphics[width=.75\textwidth]{images/hmpresencesensor-flow.png}
\caption{Flow für den Homematic IP Präsenzmelder}
\label{fig:hmpresencesensor-flow}
\end{figure}
In diesem Flow (s. Abb. \ref{fig:hmpresencesensor-flow}) wird die Logik von dem HomeMatic IP Präsenzmelder definiert.
Zu Beginn wird eine Subscription durch die beiden violetten Nodes (linke Seite) erstellt, somit erhalten wir die Daten von vorgenanntem Sensor.
Je nachdem, welchen Wert wir erhalten wird dementpsrechend eine Weiterverarbeitung durch Function Nodes vorgenommen und anschließend durch einen Text auf der Bedienoberfläche dargestellt.
\subsection{HMDoorSensor-Flow}
\label{kevin:hmdoorsensor-flow}
\begin{figure}[H]
\centering
\includegraphics[width=.75\textwidth]{images/hmdoorsensor-flow.png}
\caption{HomeMatic Türsensor Flow}
\label{fig:hmdoorsensor-flow}
\end{figure}
Der HomeMatic Funk Tür-/Fensterkontakt liefert durch die Subscription „DoorSensor“ (violetterNode) einen Boolean-Wert, ob die Türe offen oder geschlossen ist.
Der weitergeleitete Wert wird in der „Function“ Node durch den String „Geschlossen“ oder „Offen“ ersetzt und als Text in der Bedienoberfläche dargestellt.
\subsection{HMPowermeter–Flow}
\label{kevin:hmpowermeter-flow}
\begin{figure}[H]
\centering
\includegraphics[width=.75\textwidth]{images/hmpowermeter-flow.png}
\caption{Sensorauswertung für den Homematic Funk-Schaltaktor}
\label{fig:hmpowermeter-flow}
\end{figure}
Der Homematic Funk-Schaltaktor bietet die Funktionalität Daten zu senden und zu empfangen.
Die blaue Markierung beschreibt die Funktion Daten durch die Subscriptions (violetten Nodes) zu empfangen.
Dabei werden die Werte Netzspannung, Strom, Leistung und Frequenz auf der Bedienoberfläche dargestellt.
Die Netzspannung und der Strom haben zusätzlich eine grafische Auswertung erhalten, um dadurch den zeitlichen Verlauf der Werte besser verfolgen zu können.
Für das Ein-/ Ausschalten des Funk-Schaltaktors wird ein Switch-Schalter innerhalb der Bedienoberfläche erstellt.
Das Schalten erfolgt durch die Booleanwerte True/False und wird anschließend mit einem sogenannten Thema/Topic zu RedMatic weitergeleitet.
Der„StateText“ Node zeigt in der Bedienoberfläche den aktuellen Verbindungsstatus des Homematic Moduls an.
\begin{figure}[H]
\centering
\includegraphics[width=.75\textwidth]{images/hmpowermeter-redmatic-flow.png}
\caption{Weiterleitungen in RedMatic}
\label{fig:hmpowermeter-redmatic-flow}
\end{figure}
Die Weiterleitung der Sensordaten an Node-RED erfolgt in RedMatic in Abschnitt \ref{lennart:redmatic} auf Seite \pageref{lennart:redmatic}.
Wie bei der blauen Markierung erkennbar ist, werden in diesem Flow die Sensordaten des Homematic Modulsdirekt an Node-RED weitergeleitet und dort empfangen.
Die rote Markierung zeigt die Logik für das Umschalten des Funk-Schaltkontaktes.
Zu Beginn wird eine Subscription auf Node-RED gemacht, welche die Daten des Switch-Schalters empfängt.
Anschließend werden die empfangenen Boolean-Werte an das Modul gesendet und umgesetzt.
+53
View File
@@ -0,0 +1,53 @@
\section{Subflow}
\label{kevin:subflow}
Mit der Funktion Subflow bietet Node-RED die Möglichkeit, kleinere untergeordnete Flows zu erstellen die in einem Projekt auf verschiedenen Arbeitsblättern wiederverwendet werden können.
Durch Subflows können komplexere Abläufe und Auswertungen in einem Node abstrahiert werden.
Größere Projekte werden dadurch vereinfacht und klarer strukturiert.
\subsection{PirSensor–Subflow}
\label{kevin:pir-subflow}
\begin{figure}[H]
\centering
\includegraphics[width=.75\textwidth]{images/pir-subflow.png}
\caption{Subflow PIR-Sensor}
\label{fig:pir-subflow}
\end{figure}
Für die Anzeige der Sensordaten in einer Time-Chart, wird eine Subscription durchgeführt um die Daten zu empfangen.
Durch den anschließenden „NachrichtenBegrenzung“ Node wird nur eine bestimmte Anzahl an Daten pro Zeiteinheit weitergeleitet, überschüssige Daten werden verworfen.
Die folgend „PirFunction“ filtert die Daten und leitet vereinfachte Werte weiter an das TimeChart und an den Ausgang des Subflows.
\lstinputlisting{code/pirfunction.js}
Das „PirFunction“ Node wertet die eintreffenden Daten bei der \inline{if} Anweisung aus und erstellt ein abstrahiertes Messageobjekt für die Weiterverarbeitung.
Die \inline{If-Else}-Funktion in den Zeilen 3-17 und 18-32 wird unterschieden ob ein PIR-Sensor ausgelöst hat oder nicht, demnach wird ein neues Objekt mit dem Namen \inline{newMsg} erstellt.
Mit \inline{return newMsg} werden die Daten weiter an den Ausgang und der Time-Chart geschickt.
\subsection{UltraSchallSensor -Subflow}
\begin{figure}[H]
\centering
\includegraphics[width=.75\textwidth]{images/ultraschall-subflow.png}
\caption{Ultraschallsensor Subflow}
\label{fig:ultraschall-subflow}
\end{figure}
Das Arbeitsblatt für den „UltraSchallSensor“ wurde in vier logische Untergruppen eingeteilt (siehe farbige Markierungenin Abb. \ref{fig:ultraschall-subflow}).
Innerhalb der grünen Markierung werden die Ultraschallsensordaten durch die Subscription empfangen.
Durch das anschließende „Function“ Node wird der Datenstrom aufgeteilt und die Sensoren einzeln ausgewertet.
Durch die \inline{Sensor2_I} und \inline{Sensor1_A} Funktion werden die einzelnen Datenströme ausgewertet.
Da der eine Ultraschallsensor eine zu große Schwankung aufwies ist dieser nicht weiter mit in die Auswertung der Logik eingegangen, dadurch wurde in der \inline{Sensor1_A} Funktion eine Weiterleitung mit \inline{return 0} definiert.
\paragraph{}
Die türkise Markierung, unten rechts, definiert die Auswertungschart.
\paragraph{}
Die Graue Markierung verbindet die Ausgabedaten von dem Ultraschallsensor und HomeMatic Funk Tür-/Fensterkontakt.
Durch das „merge“ Node werden die beiden Datenströme zusammengeführt und in der „OutputFunction“ verarbeitet.
Nach der Verarbeitung werden die Ergebnisse an den Output geleitet.
\lstinputlisting{code/sensor2_l.js}
Die „Sensor2\_I“ Function verarbeitet von einem der beiden Ultraschall\-sensoren die Daten.
Innerhalb der Zeilen 4 und 5 werden zuerst die Konstanten festgelegt, welche die Distanz und eine Schwankungstoleranz definieren.
In den Zeilen 9 bis 22 wird durch eine Kontrollstruktur geprüft, ob jemand den Ultraschallsensor passiert hat.
Sobald eine Person vorbeigelaufen ist, wird ein neues Message-Objekt erstellt und mit den dem payload-Wert true gefüllt.
\lstinputlisting{code/sensor2_l-output.js}
Die „OutputFunction“ beschreibt die Logikauswertung der Daten von dem HomeMatic Funk Tür-/ Fensterkontakt und des Ultraschallsensors.
Durch die Überprüfung mit der Kontrollstruktur wird definiert, wenn die Türe geöffnet ist und jemand den Ultraschallsensor passiert.
+2
View File
@@ -0,0 +1,2 @@
\section{Vergleich zu anderen IoT Tools}
\label{kevin:vergleich}
+8
View File
@@ -0,0 +1,8 @@
\section{Node-RED Fazit}
\label{kevin:fazit}
Mit dem Werkzeugbaukasten Node-RED ist es möglich innerhalb von kürzester Zeit optisch ansprechende Dashboards zu erstellen.
Die große Auswahl an Bausteinen bietet eine gute Voraussetzung für das Entwerfen übergreifender Anwendungen.
Das Spektrum reicht von der Ansteuerung von Hardware bis zu High-Level-APIs wie Social Media Plattformen.
Node-RED kann durch eigene Scripte erweitert werden, diese werden in der Programmiersprache JavaScript geschrieben.
Für die Projektarbeit hat sich Node-RED sehr gut bewährt, weil keine expliziten Programmierkenntnisse vorausgesetzt sind.
Weiterhin stellt der Plattform-Anbieter ausführliche Anleitungen und Tutorials bereit.
+33
View File
@@ -0,0 +1,33 @@
var newMsg;
if(msg.payload === "10" || msg.payload === "00")
{
if(msg.payload === "10")
{
newMsg = {
topic: "Pir Sensor 1 & 2",
payload: 1 }
}
else
{
newMsg = {
topic: "Pir Sensor 1 & 2",
payload: 0 }
}
}
else if(msg.payload === "01" || msg.payload === "11")
{
if(msg.payload === "11")
{
newMsg = {
topic: "Pir Sensor 1 & 2",
payload: 1 }
}
else
{
newMsg = {
topic: "Pir Sensor 1 & 2",
payload: 0 }
}
}
return newMsg;
+41
View File
@@ -0,0 +1,41 @@
var newMsg;
if(Array.isArray(msg.payload))
{
if(msg.payload[0].topic === "UltraschallSensor_Output" /* && msg.payload[1].topic === "DoorSensor" */)
{
if(msg.payload[0].state === true /* && msg.payload[1].state === true */)
{
newMsg = { topic: "Output",
val: 1,
state: true }
return newMsg;
} else {
newMsg = { topic: "Output",
val: 0,
state: false }
return 0;
}
} else if(msg.payload[1].topic === "UltraschallSensor_Output" /* && msg.payload[1].topic === "DoorSensor" */)
{
if(msg.payload[0].state === true /* && msg.payload[1].state === true */)
{
newMsg = { topic: "Output",
val: 1,
state: true }
return newMsg;
} else {
newMsg = { topic: "Output",
val: 0,
state: false }
return 0;
}
} else {
newMsg = { topic: "Output", val: 0, state: false }
return 0;
}
}
+23
View File
@@ -0,0 +1,23 @@
//'Innerer' Ultraschall-Sensor (Sensor Richtung Raum)
//Info: Neue Message wird weitergeleitet. Auswertung mit Bedingungsprüfung
const Distanz = 115; //eingestellte Entfernung (Programmierung in ArduinoStudio)
const Toleranz = 10; //Schwankungs-Toleranz
var newMsg; //Neue Message für neue Werte erstellen
if (msg.payload < (Distanz - Toleranz))//ausgelöst
{
newMsg = {
topic: "UltraschallSensor_Innen",
payload: true,
val: msg.payload } //msg.payload -> aktuellen Wert mitliefern
}
else
{
newMsg = {
topic: "UltraschallSensor_Innen",
payload: false,
val: msg.payload } //msg.payload -> aktuellen Wert mitliefern
}
return newMsg;
+9
View File
@@ -0,0 +1,9 @@
% Details der Arbeit
\title{Präsenzerkennung im Rahmen einer modularen Smarthome Umgebung}
\author{Sebastian Dohle, Lennart Heimbs, Kevin Holzschuh, Johannes Krug}
\date{März-September 2019}
\betreuer{Prof.-Dr. Oliver Hofmann, B.-Eng. Martin Wimmer}
\semester{Sommersemester 2019}
\keywords{Smarthome, Raspberry Pi, Homematic, Homegear, Node-Red, Präsenz\-erkennung, ESP8266, MQTT}
Binary file not shown.

After

Width:  |  Height:  |  Size: 347 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 290 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 241 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 293 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 481 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 708 KiB

+49
View File
@@ -0,0 +1,49 @@
\usepackage{listings, xcolor, realboxes, xpatch}
\lstset{literate=
{á}{{\'a}}1 {é}{{\'e}}1 {í}{{\'i}}1 {ó}{{\'o}}1 {ú}{{\'u}}1
{Á}{{\'A}}1 {É}{{\'E}}1 {Í}{{\'I}}1 {Ó}{{\'O}}1 {Ú}{{\'U}}1
{à}{{\`a}}1 {è}{{\`e}}1 {ì}{{\`i}}1 {ò}{{\`o}}1 {ù}{{\`u}}1
{À}{{\`A}}1 {È}{{\'E}}1 {Ì}{{\`I}}1 {Ò}{{\`O}}1 {Ù}{{\`U}}1
{ä}{{\"a}}1 {ë}{{\"e}}1 {ï}{{\"i}}1 {ö}{{\"o}}1 {ü}{{\"u}}1
{Ä}{{\"A}}1 {Ë}{{\"E}}1 {Ï}{{\"I}}1 {Ö}{{\"O}}1 {Ü}{{\"U}}1
{â}{{\^a}}1 {ê}{{\^e}}1 {î}{{\^i}}1 {ô}{{\^o}}1 {û}{{\^u}}1
{Â}{{\^A}}1 {Ê}{{\^E}}1 {Î}{{\^I}}1 {Ô}{{\^O}}1 {Û}{{\^U}}1
{Ã}{{\~A}}1 {ã}{{\~a}}1 {Õ}{{\~O}}1 {õ}{{\~o}}1
{œ}{{\oe}}1 {Œ}{{\OE}}1 {æ}{{\ae}}1 {Æ}{{\AE}}1 {ß}{{\ss}}1
{ű}{{\H{u}}}1 {Ű}{{\H{U}}}1 {ő}{{\H{o}}}1 {Ő}{{\H{O}}}1
{ç}{{\c c}}1 {Ç}{{\c C}}1 {ø}{{\o}}1 {å}{{\r a}}1 {Å}{{\r A}}1
{€}{{\euro}}1 {£}{{\pounds}}1 {«}{{\guillemotleft}}1
{»}{{\guillemotright}}1 {ñ}{{\~n}}1 {Ñ}{{\~N}}1 {¿}{{?`}}1
}
\definecolor{codegreen}{rgb}{0,0.6,0}
\definecolor{codegray}{rgb}{0.5,0.5,0.5}
\definecolor{codepurple}{rgb}{0.58,0,0.82}
\definecolor{backcolour}{rgb}{0.95,0.95,0.92}
\lstdefinestyle{listingstyle}{
backgroundcolor=\color{backcolour},
commentstyle=\color{codegreen},
keywordstyle=\color{magenta},
numberstyle=\tiny\color{codegray},
stringstyle=\color{codepurple},
basicstyle=\ttfamily\footnotesize,
breakatwhitespace=false,
breaklines=true,
captionpos=b,
keepspaces=true,
numbers=left,
numbersep=5pt,
showspaces=false,
showstringspaces=false,
showtabs=false,
tabsize=2
}
\lstset{style=listingstyle}
\def\inline{\lstinline[basicstyle=\ttfamily\footnotesize]}
\makeatletter
\xpretocmd\lstinline{\Colorbox{backcolour}\bgroup\appto\lst@DeInit{\egroup}}{}{}
\makeatother
Binary file not shown.
+46
View File
@@ -0,0 +1,46 @@
\documentclass[11pt]{scrartcl}
\usepackage[utf8]{inputenc}
\usepackage[a4paper, right=2cm, top=2cm, left=3cm, bottom=3cm]{geometry}
\usepackage[ngerman]{babel}
\usepackage{arbeit}
\usepackage{acronym}
\usepackage{xpatch}
\usepackage{standalone}
\usepackage{tikz}
\usepackage[ngerman]{babel}
\usepackage{float}
\usepackage{subcaption}
\usepackage{tikz}
\usepackage{graphicx}
\usetikzlibrary{decorations.pathreplacing}
\usetikzlibrary{positioning}
\definecolor{codegreen}{rgb}{0,0.6,0}
\definecolor{codegray}{rgb}{0.5,0.5,0.5}
\definecolor{codepurple}{rgb}{0.58,0,0.82}
\definecolor{backcolour}{rgb}{0.95,0.95,0.92}
\input{includes}
%% Beginn des eigentlichen Dokumentes
%%-----------------------------------
\begin{document}
\makeatletter
\xpretocmd\lstinline{\Colorbox{backcolour}\bgroup\appto\lst@DeInit{\egroup}}{}{}
\makeatother
\tableofcontents
\newpage
\input{chapters.tex}
%\newpage
%\bibliographystyle{plain}
%\bibliography{references}
\end{document}
+43
View File
@@ -0,0 +1,43 @@
@misc {bendel2019smarthome,
author = "Prof. Dr. Oliver Bendel",
title = "Smart Home",
year = "2019",
month = "Januar",
howpublished = {https://wirtschaftslexikon.gabler.de/definition/smart-home-54137/version-368820},
note = "[Online; aufgerufen 07-08-2019]"
}
@misc {mayer2017smarthomesetup,
author = "Patrik Mayer",
title = "Homematic mit node-red über homegear",
year = "2017",
month = "Juli",
howpublished = {https://allgeek.de/2017/07/09/homematic-mit-node-red-ueber-homegear/},
note = "[Online; aufgerufen 27-08-2019]"
}
@misc {homegear2018documentation,
author = "Homegear",
title = "Homegear Documentation",
year = "2016-2018",
howpublished = {https://doc.homegear.eu/overview/},
note = "[Online; aufgerufen 28-08-2019]"
}
@misc {alexreinert2019pivccusetup,
author = "Alexander Reinert",
title = "piVCCU Raspberry Pi Setup",
year = "2019",
month = "Juni",
howpublished = {https://github.com/alexreinert/piVCCU/blob/master/docs/setup/raspberrypi.md},
note = "[Online; aufgerufen 09-09-2019]"
}
@misc {sebastianraff2019redmaticinstallation,
author = "Sebastian Raff",
title = "Installation",
year = "2019",
month = "März",
howpublished = {https://github.com/rdmtc/RedMatic/wiki/Installation},
note = "[Online; aufgerufen 10-09-2019]"
}
+127
View File
@@ -0,0 +1,127 @@
%%
%% LaTeX-Vorlage fuer Projektarbeit
%%
%% Neue User-Befehle
%%
%% \title Titel der Projektarbeit
%% \semester z.B. Wintersemester 2008/2009
%% \betreuer Kommagetrennte Liste der Betreuer
%% \maketitle Erzeugen der Titelseite
%% \keywords Schlüsselwörter angeben
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{arbeit}[2014/10/09 Paket fuer Projektarbeiten]
\RequirePackage{scrpage2}
\RequirePackage{hyperref} % Hyperref möglichst spät laden. Vielleicht hier?
% Kopf- und Fußzeilen setzen
\clearscrheadfoot
\pagestyle{scrheadings}
\cfoot{\thepage}
%% Schriftalternativen zu dem Latex-Standard: Computer Modern (CM)
%%----------------------------------------------------------------
%% Englaufende Times/Helvetica/Courier laden, aber mit richtigen Skalierungen (siehe l2tabu)
%\usepackage{mathptmx}
%\usepackage[scaled=.90]{helvet}
%\usepackage{courier} % kann auch weggelassen werden
%% Palatino, genauso wenig englaufend wie CM, aber riesiger Zeilenabstand
%\usepackage{mathpazo}
%\usepackage[scaled=.95]{helvet}
%\usepackage{courier}
%% Neue User-Befehle
%%------------------
\newcommand{\keywordsline}{\paragraph{Keywords:} \@keywords}
\newcommand{\@betreuer}{}
\newcommand{\betreuer}[1]{\renewcommand{\@betreuer}{#1}}
\newcommand{\@semester}{}
\newcommand{\semester}[1]{\renewcommand{\@semester}{#1}}
\newcommand{\@keywords}{}
\newcommand{\keywords}[1]{\renewcommand{\@keywords}{#1}}
\AtBeginDocument{
\hypersetup{bookmarksnumbered=true,
pdfcreator={Adobe Acrobat Distiller},
pdfproducer={LaTex with hyperref}
}
}
% Eigentliche Ausgabe der Titelseite
\renewcommand{\@maketitle}
{%
\hypersetup{%
pdftitle={\@title},
pdfsubject={Projektarbeit \@semester},
pdfauthor={\@author},
pdfkeywords={\@keywords},
}%
\begin{center}
\vskip 2em%
{\titlefont\huge \@title \par}%
\vskip 2em%
{\large\textbf{Projektarbeit}\par}%
{\large \@author\par}%
\vskip 2em%
{\large Fakultät Elektrotechnik Feinwerktechnik Informationstechnik, \\
Technische Hochschule Nürnberg Georg Simon Ohm,\\
Wassertorstraße 10,\\
90489 Nürnberg, Germany\\
Betreuer: \@betreuer\par}%
\vspace*{3em} %
\end{center}%
}
% Aus scrartcl.cls extrahiert, sorgt fuer richtiges Setzen bei ein-/zweispaltigem Satz
\renewcommand{\maketitle}{
\if@twocolumn
\ifnum \col@number=\@ne
\@maketitle
\else
\twocolumn[\@maketitle]%
\fi
\else
\newpage
\global\@topnum\z@
\@maketitle
\fi
}
% Aus scrartcl.cls genommen und angepasst -> Kursive Schrift
\renewenvironment{abstract}{%
\if@twocolumn\if@abstrt %Zweispaltig
\addsec*{\abstractname}
\it% Abstract kursiv setzen
\fi
\it% Abstract kursiv setzen
\else% Einspaltig
\if@abstrt
\small
\begin{center}
{\normalfont\sectfont\nobreak\abstractname
\vspace{-.5em}\vspace{\z@}}%
\end{center}
\fi
\quotation
\fi
}{%
\if@twocolumn\else\endquotation\fi
}
% Wir wollen Latex beim Setzen des Literaturverzeichnisses
% nicht ganz so pingelig sein lassen, da wir enge Spalten verwenden
\newcommand{\orig@bibliography}{}
\let\orig@bibliography\bibliography % Alte Definition in orig@... speichern
\renewcommand{\bibliography} % \bibliography neu definieren
{
\tolerance 1414 % Toleranzwerte setzen (siehe l2tabu)
\hbadness 1414 % ...
\emergencystretch 1.5em % ...
\hfuzz 0.3pt % ...
\widowpenalty=10000 % ...
\vfuzz \hfuzz % ...
\raggedbottom % ...
\orig@bibliography % und das Originalkommando aufrufen
}
+9
View File
@@ -0,0 +1,9 @@
% Hier werden die Kapitel eingebunden
% Bei Bedarf weitere Kapitel eintragen
\input{chapters/1_introduction.tex}
\input{chapters/2_hardware.tex}
\input{chapters/3_network.tex}
\input{chapters/4_software.tex}
\input{chapters/5_fazit.tex}
\input{chapters/6_ausblick.tex}
\input{chapters/kamera_personenerkennung.tex}
+4
View File
@@ -0,0 +1,4 @@
In der Projektarbeit wird zunächst eine individuelle Smarthome Umgebung aufgesetzt. Innerhalb dieser Umgebung sollen diverse kommerzielle, sowie eigens Entwickelte Sensoren nutzbar sein. Die Kontrolle der Sensoren und Aktoren soll über eine Web-Oberfläche hochschulintern möglich sein.
Anhand der Sensoren soll anschließend ein System aus Sensoren zur Erkennung und Zählung von anwesenden Personen im Labor entwickelt werden, um einen Überblick über die Auslastung des Raumes zu erhalten.
Dabei wird ein kommerzieller Sensor für die Präsenzerkennung mit dem im Rahmen der Projektarbeit entwickelten Erkennungssystem und einer Kamera verglichen.
@@ -0,0 +1,31 @@
\section{Einleitung}
\label{sec:intro}
\paragraph{}
Intelligente Heim Überwachung und Automatisierung gewinnt stetig an Bedeutung in einer zunehmend vernetzten Gesellschaft.
Jüngste Fortschritte in der Mikrocomputertechnik, deren Kosten und in der Netzwerktechnik machen es immer erschwinglicher für Privatpersonen ihr eigenes Heim mit Sensoren zu Überwachen.
Neben Bewegungssensoren für die Außenbeleuchtung oder Überwachungskameras gibt es heute eine Vielzahl an Möglichkeiten der Heim Überwachung und Automatisierung. \\
So kann man zum Beispiel steuerbare Rollläden zusammen mit Temperatur-, Lichtintensitäts- und weiteren Sensoren nutzen um diese automatisch hoch, beziehungsweise herunter zu fahren oder Lichter einschalten, sobald sich eine Person in einem dunklen Raum befindet.
\paragraph{}
Aus diesen Möglichkeiten der Steuerung und Datenerfassung durch Sensoren ist ein breiter Markt an Systemen entstanden, die Technik für das Smarthome bereitstellen.
Diese Technik gliedert sich allgemein in drei Geräte Kategorien:
\begin{itemize}
\item Sensoren, die zur Erfassung von physikalischen Umweltdaten dienen,
\item Aktoren, die mit der Umwelt interagieren und
\item Steuerzentralen, auch Gateways genannt, welche gesammelte Daten darstellen und anhand dieser Daten Aktoren steuern.
\end{itemize}
Das Zusammenspiel dieser drei Geräte stellt die Intelligenz des Smarthome Systems dar.\\
Auf dem Markt wird die Anzahl solcher Systeme immer größer, was sich in einer besseren Auswahl für den Konsumenten auswirkt.
Allerdings sind diese Systeme in der Regel in ihrem Aufbau geschlossen und benutzen ein eigenes Ökosystem.
Sie funktionieren nur mit Geräten eines Herstellers.
Beispielhaft für ein solches geschlossenes System ist Homematic von der Firma eQ-3.
Der Hersteller ist einer der Marktführer im Bereich Smarthome in Europa und bietet mit Homematic und dem neuen System Homematic IP zwei umfangreiche Smarthome Systeme mit einer sehr großen Auswahl an Geräten an.
Homematic Sensoren und Aktoren können jedoch nur mit der hauseigenem Smarthome Zentrale (Central Control Unit, CCU) betrieben werden.\\
Aufgrund von zum Teil deutlichen Preisunterschieden zwischen verschiedenen Herstellern ist es wünschenswert diese miteinander kombinieren zu können.
Zusätzlich ist es möglich eigene Sensoren sehr preiswert zu entwickeln, die wiederum nicht mit den kommerziellen Sensoren kommunizieren können.
Aus diesem Grund ist ein Ziel dieser Projektarbeit ein Smarthome System aufzusetzen, welches es möglich macht, eine Vielzahl verschiedener Produkte verschiedener Hersteller, sowie selbstgebaute Sensoren zu verwenden.
Um die Funktionalität dieses Systems zu testen wird anschließend ein Homematic IP Sensor zur Präsenzerkennung mit eigens entwickelten Sensoren zur Anwesenheitserkennung von Personen im Labor und einer Kamera, die genutzt wird, um Personen zu zählen, verglichen.
\newpage
+24
View File
@@ -0,0 +1,24 @@
\section{Hardware der Smarthome Umgebung}
\label{blog}
Den Startpunkt des Projekts stellt der Blogeintrag ``Homematic mit Node-Red über homegear'' von Patrik Mayer \cite{mayer2017smarthomesetup} dar, in dem eine Smarthome Struktur basierend auf einem Raspberry Pi beschrieben wird.
Im Folgendem wird das Aufsetzen der Gateway unter Berücksichtigung des Blogartikels beschrieben.
Begonnen wird mit der Hardware, die für die Gateway benötigt wird und deren Konfiguration.
\paragraph{}
\label{raspberry-pi}
Als Basis für die Gateway wird der Einplatinencomputer \emph{Raspberry Pi} aufgrund seiner guten Verfügbarkeit und seines günstigen Preises gewählt.
Mit LAN, WLAN und diversen weiteren Schnittstellen wie UART und SPI und dank seiner geringen Größe ist dieser Computer sehr gut geeignet für den Einsatz als Smarthome Zentrale.
Für das Betriebssystem des Raspberry Pi wird Raspbian Stretch Lite gewählt.
Dies ist ein für den Dauerbetrieb geeignetes Betriebssystem basierend auf der Linux-Distribution Debian Stretch.
Es ist speziell für den Raspberry Pi konfiguriert und verzichtet auf eine Desktop Umgebung, wodurch der Speicher- und Rechenleistungsverbrauch minimiert wird.
Die Installation des Betriebssystems erfolgt über die bereits auf dem Raspberry Pi vorinstallierte Installationsanwendung NOOBS, welche das Betriebssystem automatisch auf der SD-Karte des Raspberry Pi herunterlädt und installiert.
\paragraph{}
\label{funkmodul}
Um mit Sensoren von kommerziellen Herstellern kommunizieren zu können wird zusätzlich ein Funkmodul benötigt.
Üblicherweise werden dabei die ISM-Bänder 433MHz und 868MHz verwendet.
Da Homematic das 868MHz-Band zur Kommunikation nutzt, wird der Bausatz \textit{HM-MOD-RPI-PCB} von dem Hersteller ELV verwendet.
Das Funkmodul wird auf die GPIO-Pins des Raspberry Pis gesteckt und benutzt das UART-Protokoll, um mit dem Raspberry Pi zu kommunizieren.
Mit dem Befehl \inline{sudo raspi-config} kann UART unter dem Menüpunkt \inline{5 Interfacing Options} und anschließend \inline{P6 Serial} aktiviert werden.
Ist UART einmal aktiviert, ist das Modul Einsatzfähig.
+66
View File
@@ -0,0 +1,66 @@
\section{Netzwerkkonfiguration des Raspberry Pi}
Die Netzwerkkonfiguration des Projektes birgt zwei Herausforderungen:
\begin{enumerate}
\item Erreichbarkeit der gewonnenen Daten, beziehungsweise Steuerung der Aktoren über das Hochschulnetzwerk
\item Kommunikation der selbstgebauten Sensoren mit der Gateway über WLAN
\end{enumerate}
Die Erreichbarkeit der Daten wird sichergestellt, indem der Raspberry Pi dauerhaft über LAN an das Hochschulnetz angeschlossen ist. Zudem benötigt er eine bekannte statische IP-Adresse um auf die Daten zugreifen zu können.
Die statische IP-Adresse wird eingestellt, indem die Datei \inline{/etc/dhcpcd.conf} folgender Abschnitt für den LAN-Adapter eingefügt wird:
\label{staticIP}
\lstinputlisting[firstline=1, lastline=16]{code/dhcpcd.tex}
Um die später beschriebenen selbstgebauten Sensoren über WLAN mit dem Gateway kommunizieren zu lassen, wurde zunächst versucht, das Hochschulnetzwerk \textit{Eduroam} zu werenden.
Da dieses jedoch die Sicherheitskonfiguration WPA2 Enterprise nutzt und dieses auf dem benutzten Mikrocontroller \textit{ESP8266} nicht unterstützt wird, muss ein alternatives WLAN-Netzwerk genutzt werden.
Als Lösung wurde die Funktionalität des Raspberry Pis genutzt, als WLAN-Accesspoint zu dienen.
Der Raspberry Pi, der per LAN mit dem Hochschulnetzwerk verbunden ist, spannt somit ein unabhängiges, lokales WLAN-Netzwerk auf, welches die WPA2-PSK Verschlüsselung benutzt, die auch von dem ESP8266 unterstützt wird.
\subsection{Raspberry Pi als Accesspoint}
Realisiert wird der Accesspiont durch das Softwarepaket \textit{hostapd}, welches durch folgendes Kommando installiert wird:
\begin{lstlisting}[language=bash]
sudo apt install hostapd
\end{lstlisting}
Danach wird hostapd für die Konfigurationsphase deaktiviert:
\begin{lstlisting}
sudo systemctl stop hostapd
\end{lstlisting}
\subsection{Raspberry Pi als Server}
Da das Netzwerk als Server agieren soll, wird dem Raspberry Pi eine statische IP-Adresse zugewiesen. Dazu wird in der Datei \inline{/etc/dhcpcd.conf} der WLAN-Adapter \inline{wlan0} folgendermaßen konfiguriert:
\lstinputlisting[firstline=18, lastline=29]{code/dhcpcd.tex}
Um die Änderungen zu übernehmen wird der dhcp daemon neu gestartet:
\begin{lstlisting}
sudo service dhcpcd restart
\end{lstlisting}
\subsection{DHCP Server Konfigurieren}
Damit die Verbindung des ESP8266 mit dem Raspberry Pi unkompliziert abläuft wird ein DHCP Server eingerichtet.
Dazu wird folgende Konfiguration in die Datei \inline{/etc/dnsmasq.conf} geschrieben:
\begin{lstlisting}
interface=wlan0
dhcp-range=192.168.252.2,192.168.252.20,255.255.255.0,24h
\end{lstlisting}
Somit werden IP-Adressen für die Microkontroller automatisch vergeben.\\
Anschließend startet man den DHCP Server erneut: \inline{sudo systemctl reload dnsmasq}.
\subsection{Accesspoint Konfigurieren}
Zur Konfiguraiton des Accesspoints wird die Datei \inline{/etc/hostapd/hostapd.conf} beschrieben.\\
Wichtig dabei ist eine geeignete SSID und ein geeignetes Passwort für das Netzwerk.
\lstinputlisting{code/hostapd.tex}
Erkennbar ist die SSID \inline{smartroom} und das Passwort \inline{smarthome}.
Anschließend muss der Accesspoint Software noch die Konfigurationsdatei in der Datei\\
\inline{/etc/default/hostapd} bekannt gemacht werden:
\begin{lstlisting}
DAEMON_CONF="/etc/hostapd/hostapd.conf"
\end{lstlisting}
\subsection{Starten des Accesspoints}
Nun muss nur der Accesspoint gestartet werden und die Netzwerkkonfiguration ist abgeschlossen.
\begin{lstlisting}
sudo systemctl unmask hostapd
sudo systemctl enable hostapd
sudo systemctl start hostapd
\end{lstlisting}
+244
View File
@@ -0,0 +1,244 @@
\section{Software der Samrthome Umgebung}
Der Aufbau der Smarthome Umgebung auf dem Raspberry Pi gliedert sich in drei Ebenen:
\begin{itemize}
\item Logik Ebene
\item Transport Ebene
\item Interface Ebene
\end{itemize}
Diese Ebenen werden jeweils über bestimmte Softwarepakete realisiert, die auf dem Raspberry Pi installiert und konfiguriert werden müssen.
Die Logik Ebene dient dabei zur Programmierung der Logik und Darstellung der erfassten Daten beziehungsweise zur Steuerung der Aktoren.
Die Transport Ebene ist die Schnittstelle zwischen der Logik Ebene und der Interface Ebene.
Sie stellt sicher, dass die beiden Ebenen jeweils verlässlich strukturierte Daten erhalten.
Zusätzlich dient sie als Schnittstelle zu den Mikrocontrollern über den WLAN-Accesspoint.
Die Interface Ebene dient dem Anbinden der kommerziellen Sensoren und Aktoren.
Im Folgenden wird die Funktion, Installation und Konfiguration der einzelnen Ebenen\\
genauer erläutert und beschrieben.
\subsection{Logik Ebene}
Die Logik der Smarthome Umgebung wird durch die Software \emph{Node-Red} realisiert, die Präsenta\-tion der Daten und die Steuerung der Aktoren durch die Node-Red Erweiterung \emph{Node-Red-dashboard}.
\subsubsection{Node-Red}
Node-Red ist ein auf der Plattform node.js basierendes und in JavaScript geschriebenes grafisches Entwicklungswerkzeug.
Es ist speziell entwickelt um Hardware, APIs und Dienste mittels eines Baukasten Prinzips miteinander zu verbinden.
Somit ist es ideal für dieses Projekt um die Informationen der Sensoren zu sammeln, auszuwerten und darauf basierend Entscheidungen zu treffen.
Die Installation von Node-Red ist dank eines von Seiten des Herstellers bereitgestellten Installationsskriptes sehr einfach.
Das Skript wird mittels folgendem Befehl heruntergeladen, ausgeführt und führt anschließend den Nutzer Schritt für Schritt durch die Installation:
\begin{lstlisting}
bash <(curl -sL https://raw.githubusercontent.com/Node-Red/raspbian-deb-package/master/resources/update-nodejs-and-nodered)
\end{lstlisting}
Nach erfolgter Installation sorgt der Befehl \inline{sudo systemctl enable nodered.service} dafür, dass Node-Red bei Systemstart automatisch gestartet wird.
\inline{sudo service nodered start} startet Node-Red anschließend.
Die Entwicklungsumgebung ist ab diesem Zeitpunkt unter der in Kapitel \ref{staticIP} auf Seite \pageref{staticIP} genannten IP-Adresse und dem Port 1880 erreichbar: \inline{https://141.75.33.126:1880}.
\subsubsection{Node-Red-dashboard}
Das Node-Red-dashboard ist eine Erweiterung für Node-Red und ermöglicht die Konfiguration einer Steuerzentrale mittels Node-Red.
Es wird nach erfolgter Node-Red Installation über den Befehl \inline{npm i Node-Red-dashboard} installiert.
Das Dashboard ist ab dem Zeitpunkt unter dieser Adresse erreichbar: \inline{https://141.75.33.126:1880/ui}.
Ebefalls sind nun die Node-Red-dashboard spezifischen Nodes in Node-Red verfügbar.
Node-Red und das Node-Red-dashboard werden näher in Kapitel \ref{kevin:node-red} auf Seite \pageref{kevin:node-red} erklärt.
\subsection{Transport Ebene}
Auf der Transport Ebene wurde das Internet of Things (IOT) Protokoll \emph{MQTT} mit der Software \emph{Mosquitto} gewählt.
\begin{figure}[H]
\centering
\includestandalone[width=.75\textwidth]{standalone/pub_sub_arch}
\caption{Die Architektur von MQTT}
\label{fig:pub_sub_arch}
\end{figure}
\paragraph{Das Message Queue Telemetry Transport (MQTT)} ist ein Maschine-zu-Maschine (M2M) Nachrichtenprotokoll, entworfen nach dem Publish/Subscribe-Modell (pub/sub).
Es baut auf einem zugrundeliegendem TCP/IP Netzwerk auf und wurde speziell für M2M und mobile Anwendungen entwickelt.
Das Publish/Subscribe-Modell basiert auf einem \emph{Broker}, der ähnlich wie ein Server die zentrale Anlaufstelle der Netzwerks und für die Verteilung der Nachrichten zuständig ist.
Neben dem Broker gibt es beliebig viele \emph{Subscriber} und \emph{Publisher}.
Ein Subscriber empfängt bestimmte Nachrichten vom Broker, ein Publisher sendet Nachrichten an den Broker, wobei ein einzelnes Gerät beide Rollen einnehmen kann.
In Abbildung \ref{fig:pub_sub_arch} gibt es drei Geräte die ausschließlich Daten an den Broker senden, einen Server, der nur Daten vom Broker empfängt und einen Mikrocontroller, der sowohl Daten sendet, als auch empfängt.
\begin{figure}[H]
\centering
\includestandalone[width=.6\textwidth]{standalone/mqtt_topic}
\caption{Aufbau von MQTT Topics}
\label{fig:mqtt_topic}
\end{figure}
Das Senden der Daten an den Broker wird veröffentlichen \emph{(publish)} genannt.
Möchte ein Client Daten empfangen, muss er diese abonnieren \emph{(subscribe)}.
Dieses Abonnement wird bei erstmaliger Verbindung zum Broker in Form einer Publish-Nachricht angemeldet.
Identifikation von Nachrichten geschieht über \emph{Topics}, die von dem sendenden Gerät festgelegt werden.
Der Broker benutzt diese Topics, die die Form eines UTF-8 Strings haben, um Nachrichten der verbundenen Geräte zu filtern.
Topics bestehen aus mehereren Level, die durch einen Slash voneinander getrennt werden.
In Abbildung \ref{fig:mqtt_topic} ist beispielhaft eine in dieser Arbeit benutzte Topic gezeigt, unter der die von einem Ultraschallsensor gesammelten Daten abonniert werden.
Dabei ist zu Erkennen, wie die Topic hierachisch mit verschiednen Level aufgebaut ist:
Es wird begonnen mit einem allgemein gültigem Level.
In diesem Fall kennzeichnet dies die Hochschule (gso).
Weiterführend ist das Gebäude, der Raum und der entsprechende Sensor festgelegt.
Dadurch wird mit jedem weiteren Level eine immer genauere Herkunft der Daten festgelegt.
Die letzte Topic \inline{/#} ist eine \emph{Wildcard}.
Wildcards werden bei MQTT genutzt um mehrere Topics gleichzeitig zu abonnieren.
\inline{#} ist dabei eine \emph{Multilevel Wildcard}, die mehrere Topic Level abonniert.
So werden beispielhaft die Topics \inline{gso/bb/104/ultraschall/status} und \inline{gso/bb/104/ultraschall/status/subsensor-1} beide mit der Topic aus Abbildung \ref{fig:mqtt_topic} abonniert, wohingegen die Topic \inline{gso/bb/104/pir/status} nicht berücksichtigt wird.
Da die Multiline Wildcard bedeutet, dass alle folgenden Level uneingeschränkt abonniert werden, kann sie nur am Ende einer Topic stehen.
Neben der Multilevel Wildcard steht die Singlelevel Wildcard, repräsentiert durch das \inline{+}.
Sie kann an einem beliebigen Topic Level stehen und ersetzt nur ein einziges Level der Topic.
Sollen beispielsweise die Statusmeldungen aller Ultraschallsensoren im Gebäude BB abonniert werden, wird für den Raum die Singlelevel Wildcard benutzt: \inline{gso/bb/+/ultraschall/status}.
Die Organisation des Datenfluss übernimmt der Broker, wie in Abbildung \ref{fig:pub_sub_flow} gezeigt:
Hier sind zwei Geräte mit dem Broker verbunden, die die Rollen eines Subscribers und eines Pulishers zeigen.
\begin{figure}[H]
\centering
\includestandalone[width=.75\textwidth]{standalone/pub_sub_flow}
\caption{Der Publish/Subscribe Prozess von MQTT}
\label{fig:pub_sub_flow}
\end{figure}
Der Subscriber sendet seine Abonnement-Nachricht an den Broker und legt die abonnierte Topic fest.
Der Broker registriert diese und leitet von nun an jede Nachricht mit der entsprechenden Topic an den Client mit dem Abonnement weiter.
Abonniert zum Beispiel der Server aus Abbildung \ref{fig:pub_sub_arch} die Topic \inline{gso/bb/104/ultraschall}, registriert dies der Broker.
Veröffentlicht nun ein Sensor Daten unter der selben Topic, gehen diese zunächst beim Broker ein, der die Daten dann sofort an alle Abonnenten dieser Topic, in deisem Fall den Subscriber, weiterleitet.
Neben den bereits gezeigten Eigenschaften von MQTT gibt es noch eine Vielzahl weiterer, wie zum Beispiel die Last-Will Nachricht, die andere Geräte informiert, dass das Sendende Gerät von dem Netzwerk getrennt wurde.
Um die Komplexität der Arbeit im Rahmen zu halten wurde aber auf die Implementierung dieser Funktionen verzichtet.
\paragraph{Mosquitto} ist eine Open-Source Implementation des MQTT-Protokolls und wird in diesem Projekt aufgrund der einfachen Verfügbarkeit als Debian-Paket für den Raspberry Pi benutzt.
Es ist ein Projekt der Eclipse Foundation und da die Software in C geschrieben ist, sehr schnell, flexibel und weitreichend erhältlich.
Installiert wird das Paket auf dem Raspberry Pi mit dem Befehl:
\begin{lstlisting}
sudo apt install mosquitto
\end{lstlisting}
Dadurch werden die drei Teile des Mosquitto-Projektes installiert: Der \inline{mosquitto} Server, die \inline{mosquitto_sub} und \inline{mosquitto_pub} Anwendungen und ein MQTT-C/C++-Bibliothek-Wrapper, der hier aber nicht benutzt wird.
Um den Mosquitto Server zu starten werden die folgenden Kommandos benötigt:
\begin{lstlisting}
sudo systemctl enable mosquitto
sudo systemctl start mosquitto
\end{lstlisting}
Von nun an ist der Mosquitto Broker über die IP-Adresse des Raspberry Pis und mit dem Port 1883 erreichbar.
Um die Funktionalität zu testen können die Anwendungen \inline{mosquitto_sub} und \inline{mosquitto_pub} genutzt werden:
Mit Hilfe des Befehls
\begin{lstlisting}
mosquitto_sub -t 'test/topic' -v
\end{lstlisting}
kann man testweise eine Topic abonnieren und mit dem Befehl
\begin{lstlisting}
mosquitto_pub -t 'test/topic' -m 'hello world'
\end{lstlisting}
eine Testnachricht abschicken. Kommt die Nachricht in dem \inline{mosquitto_sub}-Fenster an, ist der MQTT-Broker einsatzbereit.
Um zu testen ob Geräte richtig an das Netzwerk angeschlossen sind, ist es ebenfalls hilfreich den mosquitto\_sub Client mit der Wildcard \# zu Nutzen um alle eingehenden Nachrichten einzusehen.
Im laufenden Betrieb läuft Mosquitto dann weitestgehend im Hintergrund und es muss sich nur um angemessene Definitionen der Topics gekümmert werden.
\subsection{Interface Ebene}
Die letzte Ebene dient der Kommunikation mit den kommerziellen Sensoren.
Da jeder Hersteller von Smarthome Geräten sein eigenes Protokoll benutzt um mit seinen Geräten zu kommunizieren, wird hier Software, die in der Lage ist verschiedene Protokolle zu sprechen, mit Hardware - den Funk-Sendern und -Empfängern - gekoppelt.
\subsubsection{Homegear}
\label{homegear}
Die in dem Blogartikel von Patrik Mayer in Kapitel \ref{blog} auf Seite \pageref{blog} genutzte Software um mit verschiedenen Aktoren und Sensoren zu kommunizieren ist \emph{homegear}.
Homegear ist ein Open-Source Programm um IoT Geräte zentral zu kontrollieren und zu verwalten.
Dazu spricht es eine Vielzahl an Protokollen diverser Hersteller wie Homematic, Intertechno, Philips Hue und Sonos, und unterstützt auch das MQTT-Protokoll.
Homegear ist dabei in der Lage sowohl als komplette Smarthome Umgebung inklusive konfigurierbaren Dashboard zu dienen als auch als bloße Schnittstelle die die Verbindung mit den kommerziellen Geräten übernimmt und die Daten über HTTP, MQTT oder anderwertig weiterleitet.
Um die Flexibilität der Umgebung zu erhöhen wird homegear hier nur als Schnittstelle genutzt.
Homegear selbst besteht aus dem Homegear-Grundmodul, welches die grundlegende Funktionalität bereitstellt und weiteren spezifischen Modulen, die die verschiedenen Hersteller/Protokolle bedienen oder das Dashboard zur Verfügung stellen.
Installiert auf dem Raspberry Pi wird das Grundmodul über das Offizielle Repository:
\begin{lstlisting}
sudo apt install apt-transport-https
wget https://apt.homegear.eu/Release.key && apt-key add Release.key && rm Release.key
sudo echo 'deb https://apt.homegear.eu/Raspbian/ stretch/' >> /etc/apt/sources.list.d/homegear.list
sudo apt update
sudo apt install homegear homegear-nodes-core homegear-management
\end{lstlisting}
Die verfügbaren Hersteller-spezifischen Module können in der Homegear Dokumentaiton \cite{homegear2018documentation} gefunden werden.
Homematic BidCos, welches das Standard Funkprotokoll von Homematic bis 2015 war, kann beispielsweise über das vorher eingerichtete Repository ganz einfach mittels \inline{apt install} installiert werden:
\begin{lstlisting}
sudo apt install homegear-homematicbidcos
\end{lstlisting}
Nach der Installation eines Moduls muss der homegear-Service mittels
\begin{lstlisting}
sudo service homegear restart
\end{lstlisting}
neu gestartet werden.
Die ordnungsgemäße Funktion des Moduls kann anschließend in der Log-Datei von homegear überprüft werden:
\begin{lstlisting}
tail -n 1000 -f /var/log/homegear/homegear.log
\end{lstlisting}
Funktioniert das Modul einwandfrei muss als nächstes das Funkmodul Homegear bekannt gemacht werden.
Für das Modul aus Abschnitt \ref{funkmodul} müssen folgende Zeilen zu der Homematic BidCos Konfigurationsdatei \inline{/etc/homegear/homematicbiscos.conf} hinzugefügt werden:
\lstinputlisting{code/bidcos.tex}
Homematic BidCos Geräte werden bei korrekter Funktion des Moduls über das Komando\-zeilen-Interface hinzugefügt.
Über \inline{homegear -r} wird das Komandozeilen-Interface gestartet.
Mittels \inline{families select 0} wird die Homematic BidCos Gerätefamilie ausgewählt.
\inline{pairing on} versetzt homegear schließlich in den Pairing-Modus.
Nun muss nur noch der Pairing-Modus an dem entsprechenden Gerät eingeschaltet werden.
Nach wenigen Sekunden können mit dem Befehl \inline{ls} die Verbundenen Geräte angezeigt werden.
Um über Node-Red mit den angeschlossenen Geräten zu kommunizieren muss das MQTT-Interface von homegear eingerichtet werden.
Dazu wird dient die Konfigurationsdatei \inline{mqtt.conf} in dem homegar Konfigurationsverzeichnis \inline{/etc/homegear}.
\subsubsection{Homematics Protokolle}
Wie in Abschnitt \ref{homegear} bereits erwähnt wurde das Standardprotokoll von Homematic \emph{Homematic BidCos} im Jahre 2015 von \emph{Homematic IP} abgelöst.
Homegear unterstützt das neue Protokoll Homematic IP jedoch nicht.
Um beispilsweise den bereits angesprochenen Präsenzsensor von Homematic nutzen zu können, der das Homematic IP Protokoll benutzt, ist es notwendig eine Alternative zu homegear zu finden.
Dazu wurden nach einer Webrecherche drei Optionen gefunden:
\begin{enumerate}
\item RaspberryMatic
\item YAHM
\item piVCCU
\end{enumerate}
\paragraph{RaspberryMatic} ist ein von Jens Maus entwickeltes Projekt, welches ein einfaches, Linux/ buildroot-basierendes Homematic kompatibles Betriebssystem für Einplatinencomputer wie den Raspberry Pi zur Verfügung stellt.
Es basiert auf der \textbf Open-\textbf Central-\textbf Control-\textbf Unit-SDK (OCCU) die von eq-3 zur freien Verfügung gestellt wird.
Damit ist RaspberryMatic in der Lage die gesamte Protokollpalette von Homematic zu bedienen.
Der Vorteil von RaspberryMatic ist die einfachheit der Bedienung und die enorme Anzahl an extra Features.
Verworfen wurde RaspberryMatic jedoch, da es nur als komplettes Betriebssystemimage installierbar ist.
Dies bedeutet, dass bei Benutzung beziehungsweise bei der Installation von RaspberryMatic die gesamte bestehende Konfiguration des Raspberry Pi zurückgesetzt wird und widerholt werden muss nachdem das neue Betriebssystem installiert ist, was sehr Aufwändig und nicht Anwenderfreundlich ist.
Zusätzlich ist der Nutzen eines Alternativ-Programmes zu homegear nur als Zwischenlösung gedacht.
Implementiert homegear das Homematic IP Protokoll in der Zukunft oder wird kein Homematic IP Gerät in der Smarthome Umgebung benutzt, ist es wünschenswert RaspberryMatic vom Raspberry Pi entfernen zu können.
Dies ist mit RaspberryMatic aber nicht ohne größeren Aufwand möglich.
Aus diesen Gründen wurde von der Benutzung von RaspberryMatic abgesehen.
\paragraph{YAHM} ist eine von Leonid Kogan entwickelte Lösung, die die Central-Control-Unit 2 (CCU2) von eq-3 in einem Virtuellen Linux Container (LXC) auf dem Raspberry Pi emuliert.
Die CCU2 wird normalerweise von eq-3 separat mit eigener Hardware verkauft, durch YAHM läuft sie als eigenständiges System auf dem Raspbery Pi.
Das Hinzufügen von Geräten geschieht bei YAHM über das Homematic eigene Webinterface und es werden wie bei RaspberryMatic alle Homematic Protokolle unterstützt.
Die Entscheidung gegen YAHM ist gefallen, da YAHM seit Juni 2018 nicht mehr aktiv Entwickelt wird und damit die Akutalität der Installation nicht gewährleistet werden kann.
\paragraph{piVCCU} ist die dritte Alternative um Homematic IP zu benutzen.
piVCCU ist ein Project von Alexander Reinert, welches wie YAHM einen Virtuellen Linux Container benutzt um die CCU2 oder auch die CCU3 (die neueste Generation der Homematic CCUs) auf dem Raspberry Pi zu emulieren.
Durch den Virtuellen Linux Container wird ebenfalls wie bei YAHM das Homematic Webinterface erreichbar gemacht, womit das Anlernen, Konfigurieren und Bedienen aller mit der CCU2 beziehungsweise CCU3 kompatiblen Homematic BidCos und Homematic IP Geräte ermöglicht wird.
piVCCU ist im Rahmen eines Debian-Repositorys für den Raspberry Pi installierbar und lässt sich damit bei Bedarf sehr einfach wieder von der Smarthome Umgebung entfernen.
Im Gegensatz zu YAHM wird an piVCCU immer noch aktiv gearbeitet, weshalb die Entscheidung auf piVCCU gefallen ist.
\subsubsection{Homematic CentralControlUnit mittels piVCCU}
Die Installation von piVCCU erfolgt anhand der auf Github bereitgestellten Installationsanleitung \cite{alexreinert2019pivccusetup}.
Dabei wird direkt die aktuelle Version 3 der CCU (CCU3) gewählt.
Nach erfolgreicher Installation wird mittels des Befehls \inline{sudo pivccu-info} in Schritt 10 die IP-Adresse der CCU ermittelt.
In diesem Fall ist sie \inline{141.75.33.250}.
Unter dieser IP-Adresse ist das Webinterface der CCU von nun an im Hochschulnetzwerk erreichbar und die Homematic Geräte können angelernt werden.
Das Anlernen und die Funktionen des Webinterfaces werden in Abschnitt \ref{dohle:homematic-anlernen} auf Seite \pageref{dohle:homematic-anlernen} genauer erläutert.
Um piVCCU vollständig in die Smarthome Umgebung einzubinden muss noch die Kommunikation mit dem MQTT Broker und Node-Red eingerichtet werden.
piVCCU stellt dafür keine eigene Schnittstelle zur Verfügung, weshalb eine Behelfslösung für die Kommunikation gewählt wurde.
Diese stellt sich in Form von RedMatic dar.
\paragraph{RedMatic} ist ein Addon für die CCU3 und RaspberryMatic, was eine Node-Red Installation auf diesen Geräten verfügbar macht.
Da durch piVCCU die originale CCU-Software auf dem Raspberry Pi emuliert wird, ist RedMatic auch unter piVCCU installierbar.
Die Nutzung von RedMatic bedeutet, dass eine zweite, eigenständige Node-Red Installation in der Smarthome Umgebung läuft, welche sich aber auf die in dem LXC-Container betriebene CCU beschränkt und nichts mit der eigentlichen Node-Red Installation auf dem Raspberry Pi zu tun hat.
Sie dient lediglich der Kommunikation der CCU über MQTT mit Node-Red auf dem Raspberry Pi.
Der Nachteil dieser Lösung ist der größere Fest- und Arbeitsspeicher Bedarf des Raspberry Pis.
Dies fällt jedoch nicht ins Gewicht, solange die Smarthome Umgebung sich auf einzelne Räume der Hochschule beschränkt und damit eine überschaubare Anzahl an Geräten angeschlossen sind.
\label{pivccu:expansion}
Bei einer größeren Expansion des Systems ist es zu Empfehlen, die Kommunikation der CCU mit dem Raspberry Pi auf Ebene des Linux Containers in Form einer TCP/IP-Kommunikation zu realisieren.
Installiert wird RedMatic nach der Installtionsanleitung für RedMatic \cite{sebastianraff2019redmaticinstallation} über die Weboberfläche der CCU.
Nach einen Neustart des Raspberry Pis ist RedMatic unter der Adresse \inline{http://141.75.33.250/addons/red} erreichbar.
Um den unberechtigten Zugriff auf RedMatic zu verhindern ist der Zugriff Passwort geschützt.
Die Zugangsdaten wurden folgendermaßen festgelegt:
\begin{lstlisting}
User: pi
Password: smarthome
\end{lstlisting}
Die Anbindung von piVCCU an den MQTT Broker geschieht mittels der RedMatic spezifischen Home\-matic-Nodes.
Wie diese Nodes Konfiguriert werden wird in Kapitel \ref{kevin:redmatic} auf Seite \pageref{kevin:redmatic} beschrieben.
\newpage
+36
View File
@@ -0,0 +1,36 @@
\section{Fazit der Smarthome Umgebung}
\label{fazit}
\begin{figure}[p]
\centering
\begin{subfigure}[b]{0.8\textwidth}
\includegraphics[width=1\linewidth]{images/node-red-homegear.jpg}
\caption{}
\label{fig:structure-old}
\end{subfigure}
\begin{subfigure}[b]{0.8\textwidth}
\includestandalone[width=1\textwidth]{standalone/final_structure}
\caption{}
\label{fig:structure-new}
\end{subfigure}
\caption{Vergleich der Smarthome Umgebungen: \ref{fig:structure-old} zeigt die von Patrik Mayer \cite{mayer2017smarthomesetup} vorgeschlagene Umgebung, \ref{fig:structure-new} zeigt die in dieser Arbeit umgesetzte Umgebung}%
\label{fig:structures}
\end{figure}
Die in dem Blogbeitrag ''Homematic mit node-red über homegear'' von Patrik Mayer \cite{mayer2017smarthomesetup} beschriebene Smarthome Umgebung konnte weitestgehend Umgesetzt werden.
Das grobe Gerüst mit Node-Red, dem Node-Red-dashboard, MQTT als Transport-Protokoll und Homegear um mit kommerziellen Systemen zu kommunizieren konnte übernommen werden.
Wo die beschriebene Gateway von der aus dem Blogbeitrag abweicht, ist die spezifische Netzwerk Konfiguration die durch die Hochschulumgebung begründet ist.
Eigens Entwickelte Sensoren können nicht das Hochschulnetzwerk benutzen, da sie die verwendete Verschlüsselung nicht unterstützen.
Stattdessen dient der Raspberry Pi als Accesspoint für die Sensoren mit einem eigenen WLAN-Netzwerk.
\paragraph{}
Zusätzlich waren zur Zeit der Veröffentlichung des Blogbeitrags im Jahr 2017 Geräte die das Protokoll Homematic IP nutzen nicht sehr weit Verbreitet.
Dies hat sich mit der Zeit geändert und Homematic IP ist mittlerweile laut Herstellerseite eines der meistgenutzten Protokolle in Europa.
Da Homegear dieses Protokoll aufgrund der fehlenden Quelloffenheit nicht unterstützt, wurden verschiedene Alternativen getestet und schließlich die originale Gateway Software von Homematic in Form von piVCCU auf dem Raspberry Pi emuliert.
Durch Nutzen von piVCCU wird die Kommunikation mit kommerziellen Sensoren zweigeteilt:
Homematic Sensoren werden über die Homematic Weboberfläche die unter piVCCU läuft angesprochen.
Verwaltung aller anderen kommerziellen Sensoren übernimmt Homegear.
Die Abbildung \ref{fig:structures} zeigt die Beschriebenen Unterschiede der beiden Smarthome Umgebungen noch einmal vergleichend auf.
+15
View File
@@ -0,0 +1,15 @@
\section{Ausblick}
\label{ausblick}
Um die entwickelte Smarthome Umgebung robuster zu gestalten, ist es wünschenswert, den bisher eingeschlagenen Weg über RedMatic abzuschaffen.
Wie in Abschnitt \ref{pivccu:expansion} auf Seite \pageref{pivccu:expansion} erwähnt, sollte der Nachrichtentransport der CCU statt über eine zweite Node-Red Installation direkt mit Hilfe eines Skriptes mittels TCP/IP an Mosquitto erfolgen.
Alternativ ist es auch denkbar die piVCCU an Homegear über die Homegear-Schnittstelle XML-RPC anzubinden.
Dazu ist aber weitere Recherche notwendig.
Idealerweise implementiert Homegear in der Zukunft das Homematic IP Protokoll und mach das Benutzen von piVCCU überflüssig.
\paragraph{}
Um die Sicherheit des Systems zu Verbessern, unterstützt MQTT die Nutzung von SSL-Verschlüsselung, wodurch Nachrichten verschlüsselt übertragen werden können.
Dies erfordert die Nutzung von SSL-Zertifikaten, die auch auf den Sensoren bekannt gemacht werden.
Außerdem ist es wünschenswert Eduroam für die WLAN-Sensoren zu nutzen.
Mit der Verbindung der WLAN-Sensoren zu dem vom Raspberry Pi aufgesetztem WLAN-Netzwerk, ist es Nötig, in jedem Raum in dem Sensoren genutzt werden sollen, einen Raspberry Pi an das LAN-Netzwerk der Hochschule anzuschließen.
Sind die Sensoren jedoch im WLAN-Netz der Hochschule reicht ein Zentraler Computer als Zentrale der Samrthome Umgebung.
@@ -0,0 +1,54 @@
\newpage
\section{Personenerkennung mit Hilfe einer Kamera}
\begin{figure}[p]
\centering
\begin{subfigure}[b]{0.8\textwidth}
\includegraphics[width=1\linewidth]{images/pi_camera_1.jpg}
\caption{}
\label{fig:kamera-activate1}
\end{subfigure}
\begin{subfigure}[b]{0.8\textwidth}
\includegraphics[width=1\textwidth]{images/pi_camera_2.jpg}
\caption{}
\label{fig:kamera-activate2}
\end{subfigure}
\begin{subfigure}[b]{0.8\textwidth}
\includegraphics[width=1\textwidth]{images/pi_camera_3.jpg}
\caption{}
\label{fig:kamera-activate3}
\end{subfigure}
\caption{Drei Aktivierungsschritte der Praspberry Pi Kamera: \ref{fig:kamera-activate1}: Interfacing Options auswählen, \ref{fig:kamera-activate2}: Camera Options auswählen, \ref{fig:kamera-activate3}: Aktivierung bestätigen}%
\label{fig:kamera-activate}
\end{figure}
Als ein weiteres Vergleichsmittel zur Personenerkennung im Labor wurde eine 5 Megapixel Kamera für den Raspberry Pi zusammen mit einem Bilderkennungs Skript genutzt.
Aus Datenschutzgründen ist die Benutzung einer Kamera zwar nicht dauerhaft möglich in der Hochschule, zum Vergleichen der Effektivität mit der in dieser Arbeit entwickelten Präsenzerkennung und dem Präsenzerkennungssensor von Homematic jedoch sehr hilfreich.
Angeschlossen wird die Kamera über ein spezielles Flachbandkabel an den Raspberry Pi.
Die Aktivierung der Kamera erfolgt über das Raspberry Pi Software Configuration Tool, das mit dem Befehl \inline{sudo raspi-config} aufgerufen wird.
Dort wird anschließend wie in Abbildung \ref{fig:kamera-activate} gezeigt die Kamera aktiviert.
Um die von der Kamera aufgenommenen Bilder zu analysieren wird Python zusammen mit den Modulen OpenCV, Numpy, Imutils und imagezmq benutzt.
Der Sourcecode ist auf dem EFI Git Service unter dem Projekt \inline{heimbsle69869 / smarthome-presence-detect} in dem Ordner \inline{Camera} zu finden.
Zur Personenerkennung wird das Skript \inline{person_detection.py} genutzt.
Es nutzt imutils um einen Videostream zu lesen, von dem jeder Frame in ein OpenCV-Array eingelesen wird.
Anhand dieses Arrays werden durch ein bereits für die Personenerkennung trainierten Tensorflow-Erkennungsmodell mit einer definierbaren Konfidenz Personen erkannt.
Ändert sich die Anzahl der erkannten Personen von einem Frame auf den anderen, wird die neue Anzahl per MQTT an Node-Red veröffentlicht.
\paragraph{}
Die Präsenzerkennung mit dieser Methode wird begrenzt duch die Rechenleistung des Rasp\-berry Pi, dem Aufnahmewinkel der Kamera und der Qualität des Trainings des verwendeten Erkennungsmodell.
Durch die begrenzte Rechenleistung des Raspberry Pi werden die aufgenommenen Frames nur sehr langsam, mit etwa 3-10 Frames pro Sekunde, verarbeitet.
Die Kamera nimmt mit 30 Frames pro Sekunde auf.
Dies kann verbessert werden, indem die Frames vom Raspberry Pi über WLAN an einen leistungsfähigeren Computer übertragen werden, auf dem die Analyse stattfindet.
So konnte die Verarbeitungsgeschwindigkeit auf fast 30 Frames pro Sekunde erhöht werden.
Das verwendete Erkennungsmodell erkennt Personen während des Tests zuverlässig.
Allerdings werden sehr häufig fälschlicherweise mehr Personen erkannt als sich im Bild befinden.
Dies könnte mit weiterem Training des Modells verbessert werden.
\paragraph{Fazit Kamera:}
Die Präsenzerkennung mit Hilfe einer Kamera ist nicht realistisch Anwendbar in einer Hochschulumgebung, da heutige Datenschutzrichlinien die Aufnahme Personenbezogener Daten ohne deren Einwilligung nicht erlauben.
Sie ist jedoch in Anbetracht der Erkennungsqualität die vielversprechendste Lösung, da mit einem nur grundlegend trainierten Erkennungsmodell bereits sehr gute Ergebnisse erzielt wurden.
+8
View File
@@ -0,0 +1,8 @@
[HomeMatic Wireless Module for Raspberry Pi]
id = My-HM-MOD-RPI-PCB
# Uncomment this if you want the HM-MOD-RPI-PCB to be your default communication module.
default = true
deviceType = hm-mod-rpi-pcb
device = /dev/ttyAMA0
responseDelay = 95
gpio1 = 18
+29
View File
@@ -0,0 +1,29 @@
interface br0
#eth0
static ip_address=141.75.33.126/24
static routers=141.75.33.1
static domain_name_servers=141.75.40.250
# It is possible to fall back to a static IP if DHCP fails:
define static profile
profile static_br0
static ip_address=192.168.1.23/24
static routers=192.168.1.1
static domain_name_servers=192.168.1.1
# fallback to static profile on eth0
interface br0
fallback static_br0
interface wlan0
static ip_address=192.168.252.1/24
nohook wap_supplicant
define static profile
profile static_wlan0
static ip_address=192.168.1.23/24
static routers=192.168.1.1
static domain_name_servers=192.168.1.1
interface wlan0
fallback static_wlan0
+16
View File
@@ -0,0 +1,16 @@
interface=wlan0
driver=nl80211
ssid=smartroom
hw_mode=g
channel=7
wmm_enabled=0
macaddr_acl=0
auth_algs=1
ignore_broadcast_ssid=0
wpa=2
wpa_passphrase=smarthome
wpa_key_mgmt=WPA-PSK
wpa_pairwise=TKIP
rsn_pairwise=CCMP
ctrl_interface=/var/run/hostapd
ctrl_interface_group=0
+9
View File
@@ -0,0 +1,9 @@
% Details der Arbeit
\title{Präsenzerkennung im Rahmen einer modularen Smarthome Umgebung}
\author{Sebastian Dohle, Lennart Heimbs, Kevin Holzschuh, Johannes Krug}
\date{März-September 2019}
\betreuer{Prof.-Dr. Oliver Hofmann, B.-Eng. Martin Wimmer}
\semester{Sommersemester 2019}
\keywords{Smarthome, Raspberry Pi, Homematic, Homegear, Node-Red, Präsenz\-erkennung, ESP8266, MQTT}
Binary file not shown.

After

Width:  |  Height:  |  Size: 236 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 209 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 KiB

+32
View File
@@ -0,0 +1,32 @@
\usepackage{listings, xcolor, realboxes, xpatch}
\definecolor{codegreen}{rgb}{0,0.6,0}
\definecolor{codegray}{rgb}{0.5,0.5,0.5}
\definecolor{codepurple}{rgb}{0.58,0,0.82}
\definecolor{backcolour}{rgb}{0.95,0.95,0.92}
\lstdefinestyle{listingstyle}{
backgroundcolor=\color{backcolour},
commentstyle=\color{codegreen},
keywordstyle=\color{magenta},
numberstyle=\tiny\color{codegray},
stringstyle=\color{codepurple},
basicstyle=\ttfamily\footnotesize,
breakatwhitespace=false,
breaklines=true,
captionpos=b,
keepspaces=true,
numbers=left,
numbersep=5pt,
showspaces=false,
showstringspaces=false,
showtabs=false,
tabsize=2
}
\lstset{style=listingstyle}
\def\inline{\lstinline[basicstyle=\ttfamily\footnotesize]}
\makeatletter
\xpretocmd\lstinline{\Colorbox{backcolour}\bgroup\appto\lst@DeInit{\egroup}}{}{}
\makeatother
Binary file not shown.
+54
View File
@@ -0,0 +1,54 @@
\documentclass[11pt]{scrartcl}
\usepackage[utf8]{inputenc}
\usepackage[a4paper, right=2cm, top=2cm, left=3cm, bottom=3cm]{geometry}
\usepackage[ngerman]{babel}
\usepackage{arbeit}
\usepackage{acronym}
\usepackage{xpatch}
\usepackage{standalone}
\usepackage{tikz}
\usepackage[ngerman]{babel}
\usepackage{float}
\usepackage{subcaption}
\usepackage{tikz}
\usepackage{graphicx}
\usetikzlibrary{decorations.pathreplacing}
\usetikzlibrary{positioning}
\definecolor{codegreen}{rgb}{0,0.6,0}
\definecolor{codegray}{rgb}{0.5,0.5,0.5}
\definecolor{codepurple}{rgb}{0.58,0,0.82}
\definecolor{backcolour}{rgb}{0.95,0.95,0.92}
\input{includes}
%% Beginn des eigentlichen Dokumentes
%%-----------------------------------
\begin{document}
\makeatletter
\xpretocmd\lstinline{\Colorbox{backcolour}\bgroup\appto\lst@DeInit{\egroup}}{}{}
\makeatother
\input{config.tex}
\maketitle
\begin{abstract}
\input{chapters/0_abstract.tex}
\keywordsline
\end{abstract}
\newpage
\tableofcontents
\newpage
\input{chapters.tex}
\newpage
\bibliographystyle{plain}
\bibliography{references}
\end{document}
+11
View File
@@ -0,0 +1,11 @@
# aussicht
-scalability: die ebenen auf verschiedenen geräten realisieren
(eg Interface sendet über netzwerk nachrichten an broker, der nachrichten an nodered weiterleitet etc)
# auswertung
-schaubild des nachrchten flows
(pivccu->broker,mcu->broker,broker->node-red, node-red->broker->pivccu)
-PORTS richtig stellen (mosquitto/node-red/node-red-dashboard)
-piVCCU wlan konfiguration
+43
View File
@@ -0,0 +1,43 @@
@misc {bendel2019smarthome,
author = "Prof. Dr. Oliver Bendel",
title = "Smart Home",
year = "2019",
month = "Januar",
howpublished = {https://wirtschaftslexikon.gabler.de/definition/smart-home-54137/version-368820},
note = "[Online; aufgerufen 07-08-2019]"
}
@misc {mayer2017smarthomesetup,
author = "Patrik Mayer",
title = "Homematic mit node-red über homegear",
year = "2017",
month = "Juli",
howpublished = {https://allgeek.de/2017/07/09/homematic-mit-node-red-ueber-homegear/},
note = "[Online; aufgerufen 27-08-2019]"
}
@misc {homegear2018documentation,
author = "Homegear",
title = "Homegear Documentation",
year = "2016-2018",
howpublished = {https://doc.homegear.eu/overview/},
note = "[Online; aufgerufen 28-08-2019]"
}
@misc {alexreinert2019pivccusetup,
author = "Alexander Reinert",
title = "piVCCU Raspberry Pi Setup",
year = "2019",
month = "Juni",
howpublished = {https://github.com/alexreinert/piVCCU/blob/master/docs/setup/raspberrypi.md},
note = "[Online; aufgerufen 09-09-2019]"
}
@misc {sebastianraff2019redmaticinstallation,
author = "Sebastian Raff",
title = "Installation",
year = "2019",
month = "März",
howpublished = {https://github.com/rdmtc/RedMatic/wiki/Installation},
note = "[Online; aufgerufen 10-09-2019]"
}
@@ -0,0 +1,72 @@
% Tikz File final_structure.tex
\documentclass{standalone}
\usepackage{tikz, graphicx}
\usetikzlibrary{decorations.pathreplacing, calc, positioning}
\begin{document}
\begin{tikzpicture}
%\draw[gray,very thin] (-3,-3) grid (12,12);
% Frame
\coordinate (frame) at (3,1);
\filldraw[color=gray!10, fill=gray!10] (frame) rectangle +(4,8.0);
\node (text_rpi) at (5,8.5) {\huge Raspberry Pi};
%node-red-dashboard
\coordinate (nrd) at (3.25,7.1);
\filldraw[color=red, fill=red] (nrd) rectangle +(3.5,0.8);
\node (text-nrd) at (5,7.5) {Node-Red-dashboard};
%node-red
\coordinate (nr) at (3.25,6.1);
\filldraw[color=red, fill=red] (nr) rectangle +(3.5,0.8);
\node (text-nr) at (5,6.5) {Node-Red};
%mosquitto
\coordinate (mq) at (3.25,4.6);
\filldraw[color=yellow, fill=yellow] (mq) rectangle +(3.5,0.8);
\node (text-mq) at (5,5) {Mosquitto};
%homegear
\coordinate (hg) at (3.25,2.1);
\filldraw[color=cyan!50, fill=cyan!50] (hg) rectangle +(3.5/2-0.25,1.8);
\node (text-hg) at (4,3) {Homegear};
%pivccu
\coordinate (pi) at (5.25,2.1);
\filldraw[color=cyan!50, fill=cyan!50] (pi) rectangle +(3.5/2-0.25,1.8);
\node (text-pi) at (6,3) {piVCCU};
%ss-dashboard
\coordinate (ss-nrd) at (9.8,8-0.5);
\node[inner sep=0pt] (img-ss-nrd) at (ss-nrd)
{\includegraphics[width=0.2\textwidth]{images/ss_dashboard.jpg}};
%ss-nodered
\coordinate (ss-nr) at (0.5,7);
\node[inner sep=0pt] (img-ss-nr) at (ss-nr)
{\includegraphics[width=0.2\textwidth]{images/ss_nodered.jpg}};
%ss-homematic
\coordinate (ss-hm) at (9.8,3);
\node[inner sep=0pt] (img-ss-hm) at (ss-hm)
{\includegraphics[width=0.2\textwidth]{images/ss_homematic.jpg}};
%esp
\coordinate (esp-c) at (0.5,3);
\node[inner sep=0pt] (esp) at (esp-c)
{\includegraphics[width=0.2\textwidth]{images/esp8266.png}};
% arrows
\draw[black,thick,<->] (text-nrd) -- (text-nr);
\draw[black,thick,<->] (text-nr) -- (text-mq);
\draw[black,thick,<->] (text-mq) -- (text-hg);
\draw[black,thick,<->] (text-mq) -- (text-pi);
\draw[black,thick,->] (esp.12) -- (text-mq.west) node[thick,midway,rotate=36,yshift=2mm,xshift=-1mm]{smarthome-Wifi};
\draw[black,thick,->] (img-ss-hm) -- (text-pi.east) node[thick,midway,yshift=2mm,xshift=1mm]{Eduroam};
\draw[black,thick,->] (img-ss-nr.-22) -- (text-nr.west) node[thick,midway,yshift=2mm,xshift=-5mm]{Eduroam};
\draw[black,thick,->] (img-ss-nrd) -- (text-nrd.east) node[thick,midway,yshift=2mm,xshift=1mm]{Eduroam};
\end{tikzpicture}
\end{document}
+24
View File
@@ -0,0 +1,24 @@
% Tikz File mqtt_topic.tex
\documentclass{standalone}
\usepackage{tikz}
\usepackage{graphicx}
\usetikzlibrary{decorations.pathreplacing}
\usetikzlibrary{positioning}
\begin{document}
\begin{tikzpicture}
\node[scale=3] (topic_104) at (0, 0) {/104};
\node[scale=3] (topic_bb) [left=-8mm of topic_104] {/bb};
\node[scale=3] (topic_gso) [left=-8mm of topic_bb] {gso};
\node[scale=3] (topic_us) [right=-8mm of topic_104] {/ultraschall};
\node[scale=3] (topic_wc) [right=-5mm of topic_us] {/\#};
\node[green,scale=1.6] (ml-wc) [above=7mm of topic_wc] {Multilevel Wildcard};
\draw[blue,decorate,decoration={brace,amplitude=12pt,pre=moveto,pre length=3mm,post=moveto,post length=3mm}] (topic_gso.north west) -- (topic_gso.north east) node[midway, above,yshift=12pt,scale=1.6]{Topic Level};
\draw[blue,decorate,decoration={brace,mirror,amplitude=12pt,pre=moveto,pre length=5mm,post=moveto,post length=4mm}] (topic_bb.south west) -- (topic_bb.south east) node[midway, below,yshift=-12pt,scale=1.6]{Topic Level};
\draw[->,thick,green] (ml-wc.south) -- (topic_wc.north) ;
\end{tikzpicture}
\end{document}
@@ -0,0 +1,84 @@
% Tikz File mqtt_pub_sub.tex
\documentclass{standalone}
\usepackage{tikz, graphicx}
\begin{document}
\begin{tikzpicture}
%\draw[help lines] (-7,-5) grid (7,5);
% Broker
\draw (0,0) circle (2);
\node[] at (0,-1.5) (broker) {\textbf{Broker}};
\node[inner sep=0pt] (broker_pc) at (0,0)
{\includegraphics[width=0.1\textwidth]{images/pc.png}};
% Publisher
\pgfmathsetmacro\xl{-6};
\pgfmathsetmacro\xr{-3};
\pgfmathsetmacro\w{(\xl+\xr)};
\pgfmathsetmacro\y{6};
% Text
\node[] at (\w/2,-\y/2-0.25) (pubs) {\textbf{MQTT Clients}};
% Images
\node[inner sep=0pt] (laptop) at (\w/2,\y/3)
{\includegraphics[width=0.1\textwidth]{images/laptop.png}};
\node[inner sep=0pt] (mcu) at (\w/2,\y/3-\y/3)
{\includegraphics[width=0.1\textwidth]{images/muc.png}};
\node[inner sep=0pt] (pc) at (\w/2,\y/3-\y/3-\y/3)
{\includegraphics[width=0.1\textwidth]{images/pc.png}};
\draw (\xl-0.25, \y/2-0.25) -- (\xl-0.25, -\y/2+0.25); % left
\draw (\xl, \y/2) arc (90:180:0.25); % top left
\draw (\xl, \y/2) -- (\xr, \y/2); % top
\draw (\xr+0.25, \y/2-0.25) arc (0:90:0.25); % top right
\draw (\xr+0.25, \y/2-0.25) -- (\xr+0.25, -\y/2+0.25); % right
\draw (\xr, -\y/2) arc (270:360:0.25); % bottom right
\draw (\xl,-\y/2) -- (\xr,-\y/2); % bottom
\draw (\xl-0.25, -\y/2+0.25) arc (180:270:0.25); % bottom left
% Subscriber
\pgfmathsetmacro\xl{3}
\pgfmathsetmacro\xr{6}
\pgfmathsetmacro\w{(\xl+\xr)};
\pgfmathsetmacro\y{6}
\node[] at (\w/2,-\y/2-0.25) (subs) {\textbf{MQTT Clients}};
% Images
\node[inner sep=0pt] (server) at (\w/2,\y/3-1)
{\includegraphics[width=0.1\textwidth]{images/server.png}};
\node[inner sep=0pt] (sub_mcu) at (\w/2,\y/3-3)
{\includegraphics[width=0.1\textwidth]{images/muc.png}};
\draw (\xl-0.25, \y/2-0.25) -- (\xl-0.25, -\y/2+0.25); % left
\draw (\xl, \y/2) arc (90:180:0.25); % top left
\draw (\xl, \y/2) -- (\xr, \y/2); % top
\draw (\xr+0.25, \y/2-0.25) arc (0:90:0.25); % top right
\draw (\xr+0.25, \y/2-0.25) -- (\xr+0.25, -\y/2+0.25); % right
\draw (\xr, -\y/2) arc (270:360:0.25); % bottom right
\draw (\xl,-\y/2) -- (\xr,-\y/2); % bottom
\draw (\xl-0.25, -\y/2+0.25) arc (180:270:0.25); % bottom left
% arrows
\draw[ultra thick, ->] (mcu) -- (broker_pc);
\draw[ultra thick, ->] (laptop) -- (broker_pc);
\draw[ultra thick, ->] (pc) -- (broker_pc);
\draw[ultra thick, ->] (sub_mcu) -- (broker_pc);
\draw[dashed, ultra thick, <-] (server) -- (broker_pc);
\draw[dashed, ultra thick, ->] (broker_pc.10) to (sub_mcu.north west);
% legend
\draw[dashed, ultra thick, ->] (-4,-4) -- (-1,-4);
\node[] (Sub) at (-2.5,-4.25) {Subscribe};
\draw[ultra thick, ->] (1,-4) -- (4,-4);
\node[] (Pub) at (2.5,-4.25) {Publish};
\end{tikzpicture}
\end{document}
@@ -0,0 +1,31 @@
% Tikz File mqtt_pub_sub.tex
\documentclass{standalone}
\usepackage{tikz, graphicx}
\begin{document}
\begin{tikzpicture}
%\draw[help lines] (-7,-5) grid (7,5);
\pgfmathsetmacro\pub{-4};
\pgfmathsetmacro\bro{0};
\pgfmathsetmacro\sub{4};
\pgfmathsetmacro\y{4};
\draw[ultra thick] (\pub,\y/2) -- (\pub,\y/-2);
\draw[ultra thick] (\bro,\y/2) -- (\bro,\y/-2);
\draw[ultra thick] (\sub,\y/2) -- (\sub,\y/-2);
\draw[ultra thick, ->] (\sub,1) -- (\bro, 1);
\draw[ultra thick, <-] (\sub,-1) -- (\bro, -1);
\draw[ultra thick, ->] (\pub,0) -- (\bro, 0);
\node at (\pub, \y/2+0.2) {Publisher};
\node at (\bro, \y/2+0.2) {Broker};
\node at (\sub, \y/2+0.2) {Subscriber};
\node at (\sub/2, 1.2) {Subscribe (topic)};
\node at (\pub/2, 0.2) {Publish (topic, info)};
\node at (\sub/2, -0.8) {Publish (topic, info)};
\end{tikzpicture}
\end{document}
+22
View File
@@ -0,0 +1,22 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Python: Current File",
"type": "python",
"request": "launch",
"program": "${file}",
"console": "integratedTerminal"
},
{
"name": "Python: Current File with args",
"type": "python",
"request": "launch",
"program": "${file}",
"console": "integratedTerminal",
"args": ["-v", "run.mp4"]
}
]
}
+3
View File
@@ -0,0 +1,3 @@
{
"python.pythonPath": "venv/bin/python"
}
Binary file not shown.
File diff suppressed because it is too large Load Diff
+45
View File
@@ -0,0 +1,45 @@
import tempfile, pexpect
def scp(src_name, dest_name, host, user, password, timeout=30, bg_run=False):
"""SSH'es to a host using the supplied credentials and executes a command.
Throws an exception if the command doesn't return 0.
bgrun: run command in the background"""
fname = tempfile.mktemp()
fout = open(fname, 'w')
options = '-q -oStrictHostKeyChecking=no -oUserKnownHostsFile=/dev/null -oPubkeyAuthentication=no'
if bg_run:
options += ' -f'
scp_cmd = 'scp %s %s@%s:%s' % (src_name, user, host, dest_name)
child = pexpect.spawn(scp_cmd, timeout=timeout, encoding='UTF-8')
child.expect(['password: '])
child.sendline(password)
child.logfile = fout
child.expect(pexpect.EOF)
# We expect any of these three patterns...
#i = child.expect (['Permission denied', 'Terminal type', '[#\$] '])
#if i==0:
# print('Permission denied on host. Can\'t login')
# child.kill(0)
#elif i==1:
# print('Login OK... need to send terminal type.')
# child.sendline('vt100')
# child.expect('[#\$] ')
#elif i==2:
# print('Login OK.')
# print('Shell command prompt', child.after)
child.close()
fout.close()
fin = open(fname, 'r')
stdout = fin.read()
fin.close()
if 0 != child.exitstatus:
raise Exception(stdout)
return stdout
+82 -12
View File
@@ -1,22 +1,92 @@
#!/usr/bin/env python2 #!/usr/bin/env python3
import time import time, subprocess
import RPi.GPIO as GPIO import RPi.GPIO as GPIO
from SCP import scp
IR_PIN = 3 IR_PIN = 3
GPIO.setmode(GPIO.BOARD) GPIO.setmode(GPIO.BOARD)
GPIO.setup(IR_PIN, GPIO.OUT) GPIO.setup(IR_PIN, GPIO.OUT)
# set cameramode to normal; GPIO.LOW would enable infrared mode
GPIO.output(IR_PIN, GPIO.HIGH)
try: def person_detect():
# initialize the HOG descriptor/person detector
hog = cv2.HOGDescriptor()
hog.setSVMDetector(cv2.HOGDescriptor_getDefaultPeopleDetector())
# loop over the image paths
for imagePath in paths.list_images(args["images"]):
# load the image and resize it to (1) reduce detection time
# and (2) improve detection accuracy
image = cv2.imread(imagePath)
image = imutils.resize(image, width=min(400, image.shape[1]))
orig = image.copy()
# detect people in the image
(rects, weights) = hog.detectMultiScale(image, winStride=(4, 4),
padding=(8, 8), scale=1.05)
# draw the original bounding boxes
for (x, y, w, h) in rects:
cv2.rectangle(orig, (x, y), (x + w, y + h), (0, 0, 255), 2)
# apply non-maxima suppression to the bounding boxes using a
# fairly large overlap threshold to try to maintain overlapping
# boxes that are still people
rects = np.array([[x, y, x + w, y + h] for (x, y, w, h) in rects])
pick = non_max_suppression(rects, probs=None, overlapThresh=0.65)
# draw the final bounding boxes
for (xA, yA, xB, yB) in pick:
cv2.rectangle(image, (xA, yA), (xB, yB), (0, 255, 0), 2)
# show some information on the number of bounding boxes
filename = imagePath[imagePath.rfind("/") + 1:]
print("[INFO] {}: {} original boxes, {} after suppression".format(filename, len(rects), len(pick)))
def main():
i = 0
try:
while True: while True:
print "Gpio Low" print("Gpio Low")
GPIO.output(IR_PIN, GPIO.LOW) time.sleep(2)
time.sleep(20)
print "Gpio High" # take image and scp it to the other pi
GPIO.output(IR_PIN, GPIO.HIGH) print("take image...")
time.sleep(20) subprocess.run(['raspistill', '-o', 'images/image' + str(i) + '.jpg'])
except Exception as e:
print e
finally: #print("transfer image")
#scp('images/image' + str(i) + '.jpg', '~/image' + str(i) + '.jpg', '192.168.252.1', 'pi', 'smarthome')
i+=1
if i>10:
break
#print("Gpio High")
#GPIO.output(IR_PIN, GPIO.HIGH)
#time.sleep(2)
# take image and scp it to the other pi
#print("take image...")
#subprocess.run(['raspistill', '-o', 'image2.jpg'])
#print("transfer image")
#scp('image2.jpg', '~/image2.jpg', '192.168.252.1', 'pi', 'smarthome')
#break
except Exception as e:
print(e)
finally:
GPIO.cleanup() GPIO.cleanup()
#person_detect()
if __name__ == '__main__':
main()
+202
View File
@@ -0,0 +1,202 @@
#!/usr/bin/env python
import argparse
import numpy as np
import cv2
import imutils
from imutils.object_detection import non_max_suppression
from video_stream import imagezmq
'''
Usage:
python peopleCounter.py -i PATH_TO_IMAGE # Reads and detect people in a single local stored image
python peopleCounter.py -c # Attempts to detect people using webcam
'''
HOGCV = cv2.HOGDescriptor()
HOGCV.setSVMDetector(cv2.HOGDescriptor_getDefaultPeopleDetector())
#HOGCV.set
VERBOSITY = False
def detector(image):
'''
@image is a numpy array
'''
clone = image.copy()
(rects, _) = HOGCV.detectMultiScale(image, winStride=(2, 2), padding=(8, 8), scale=1.05)
# draw the original bounding boxes
for (x, y, w, h) in rects:
cv2.rectangle(clone, (x, y), (x + w, y + h), (0, 0, 255), 2)
# Applies non-max supression from imutils package to kick-off overlapped
# boxes
rects = np.array([[x, y, x + w, y + h] for (x, y, w, h) in rects])
result = non_max_suppression(rects, probs=None, overlapThresh=0.65)
return result
def args_parser():
''' images, videos, remote or a local camera feed allowed
verbose for added debugging'''
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", default=None,
help="path to image test file directory")
ap.add_argument("-c", "--camera", action="store_true", default=False,
help="Set as true if you wish to use the camera")
ap.add_argument("-v", "--video", default=None,
help="path to the video file")
ap.add_argument("-r", "--remote", action="store_true", default=False,
help="video comes from remote source via imagezmq")
ap.add_argument("--verbose", action="store_true", default=False,
help="increase output verbosity")
args = vars(ap.parse_args())
if args["verbose"]:
VERBOSITY = True
return args
def usage():
print("usage: counter_people.py [-h] [-i IMAGE] [-c] [-v] [-r REMOTE] [--verbose]")
print()
print("optional arguments:")
print(" -h, --help show this help message and exit")
print(" -i IMAGE, --image IMAGE")
print(" path to image test file directory")
print(" -c, --camera Set as true if you wish to use the camera")
print(" -v, --video path to the video file")
print(" -r REMOTE, --remote REMOTE")
print(" video comes from remote source via imagezmq")
print(" --verbose increase output verbosity")
def localDetect(image_path):
result = []
image = cv2.imread(image_path)
image = imutils.resize(image, width=min(400, image.shape[1]))
if len(image) <= 0:
print("[ERROR] could not read local image")
return result
print("[INFO] Detecting people")
result = detector(image)
if VERBOSITY:
# shows the result
for (xA, yA, xB, yB) in result:
cv2.rectangle(image, (xA, yA), (xB, yB), (0, 255, 0), 2)
cv2.imshow("result", image)
cv2.waitKey(0)
cv2.destroyWindow("result")
#cv2.imwrite("result.png", np.hstack((clone, image)))
return result#(result, image)
def videoDetect(cap):
while True:
# Capture frame-by-frame
_, frame = cap.read()
if frame is None:
break
frame = imutils.resize(frame, width=min(400, frame.shape[1]))
result = detector(frame.copy())
# shows the result
for (xA, yA, xB, yB) in result:
cv2.rectangle(frame, (xA, yA), (xB, yB), (0, 255, 0), 2)
if VERBOSITY:
cv2.imshow('frame', frame)
cv2.waitKey(0)
#if time.time() - init >= sample_time:
if result:
print("{} people detected.".format(len(result)))
#init = time.time()
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# When everything done, release the capture
cap.release()
cv2.destroyAllWindows()
def remoteDetect(image_hub):
while True:
rpi_name, frame = image_hub.recv_image()
image_hub.send_reply(b'OK')
frame = imutils.resize(frame, width=min(400, frame.shape[1]))
result = detector(frame.copy())
# shows the result
for (xA, yA, xB, yB) in result:
cv2.rectangle(frame, (xA, yA), (xB, yB), (0, 255, 0), 2)
#if VERBOSITY:
cv2.imshow('frame', frame)
#cv2.waitKey(0)
#if time.time() - init >= sample_time:
if len(result):
print("{} people detected.".format(len(result)))
#init = time.time()
if cv2.waitKey(1) & 0xFF == ord('q'):
break
#cv2.imshow(rpi_name, frame) # 1 window for each RPi
#cv2.waitKey(1)
def detectPeople(args):
image_path = args["image"]
video_path = args["video"]
camera = True if args["camera"] else False
remote = True if args["remote"] else False
# Routine to read local image
if image_path is not None:
print("[INFO] Image path provided, attempting to read image")
(result, image) = localDetect(image_path)
print(str(len(result)) + " People detected.")
elif video_path is not None:
print("[INFO] Video path provided, reading video")
cap = cv2.VideoCapture(video_path)
videoDetect(cap)
# Routine to read images from webcam
elif camera:
print("[INFO] Reading images from local camera")
cap = cv2.VideoCapture(0)
videoDetect(cap)
elif remote:
print("[INFO] Reading images from remote stream")
image_hub = imagezmq.ImageHub()
remoteDetect(image_hub)
else:
usage()
def main():
args = args_parser()
detectPeople(args)
if __name__ == '__main__':
main()
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 MiB

BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 MiB

+55
View File
@@ -0,0 +1,55 @@
#!/usr/bin/env python
# import the necessary packages
from __future__ import print_function
from imutils.object_detection import non_max_suppression
from imutils import paths
import numpy as np
import argparse
import imutils
import cv2
# construct the argument parse and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--images", required=True, help="path to images directory")
args = vars(ap.parse_args())
# initialize the HOG descriptor/person detector
hog = cv2.HOGDescriptor()
hog.setSVMDetector(cv2.HOGDescriptor_getDefaultPeopleDetector())
# loop over the image paths
for imagePath in paths.list_images(args["images"]):
# load the image and resize it to (1) reduce detection time
# and (2) improve detection accuracy
image = cv2.imread(imagePath)
image = imutils.resize(image, width=min(400, image.shape[1]))
orig = image.copy()
# detect people in the image
(rects, weights) = hog.detectMultiScale(image, winStride=(4, 4),
padding=(8, 8), scale=1.05)
# draw the original bounding boxes
for (x, y, w, h) in rects:
cv2.rectangle(orig, (x, y), (x + w, y + h), (0, 0, 255), 2)
# apply non-maxima suppression to the bounding boxes using a
# fairly large overlap threshold to try to maintain overlapping
# boxes that are still people
rects = np.array([[x, y, x + w, y + h] for (x, y, w, h) in rects])
pick = non_max_suppression(rects, probs=None, overlapThresh=0.65)
# draw the final bounding boxes
for (xA, yA, xB, yB) in pick:
cv2.rectangle(image, (xA, yA), (xB, yB), (0, 255, 0), 2)
# show some information on the number of bounding boxes
filename = imagePath[imagePath.rfind("/") + 1:]
print("[INFO] {}: {} original boxes, {} after suppression".format(filename, len(rects), len(pick)))
# show the output images
if len(pick):
#cv2.imshow("Before NMS", orig)
cv2.imshow("After NMS", image)
cv2.waitKey(0)
+18
View File
@@ -0,0 +1,18 @@
"""from time import sleep
from picamera import PiCamera
camera = PiCamera()
camera.resolution = (1024, 768)
camera.start_preview()
# Camera warm-up time
sleep(2)
camera.capture('foo.jpg')
"""
import picamera
camera = picamera.PiCamera()
camera.resolution = (640, 480)
camera.start_recording('my_video.h264')
camera.wait_recording(60)
camera.stop_recording()
+235
View File
@@ -0,0 +1,235 @@
#!/usr/bin/env python
import argparse
import time
from statistics import median
import imutils
from imutils.video import VideoStream
import cv2
import numpy as np
import paho.mqtt.client as mqtt
from video_stream import imagezmq
VISUAL_DEBUG = True
BROKER = "141.75.33.126"
PORT = 1883
def getArgs():
""" Arguments """
ap = argparse.ArgumentParser()
ap.add_argument("-v", "--video", help="path to the video file")
ap.add_argument("-a", "--min-area", type=int, default=500, help="minimum area size")
return vars(ap.parse_args())
def main():
try:
mqtt_client = mqtt.Client("pi-camera")
mqtt_client.connect(BROKER, PORT)
except:
print("Connection to MQTT-Broker failed.")
return 1
try:
args = getArgs()
timer = Timer()
# if the video argument is None, then the code will read from webcam (work in progress)
if args.get("video", None) is None:
#vs = VideoStream(src=0).start()
image_hub = imagezmq.ImageHub()
time.sleep(2.0)
# otherwise, we are reading from a video file
else:
vs = cv2.VideoCapture(args["video"])
cv2.namedWindow('Video stream', cv2.WINDOW_NORMAL)
detector = DetectionFromFrame(args["min_area"], 0.8)
while True:
people_count = 0
timer.start_frame_timer()
if args.get("video", None) is None:
rpi_name, detector.currentFrame = image_hub.recv_image()
image_hub.send_reply(b'OK')
else:
detector.currentFrame = vs.read()
detector.currentFrame = detector.currentFrame if args.get("video", None) is None else detector.currentFrame[1]
# if the frame can not be grabbed, then we have reached the end of the video
if detector.currentFrame is None:
break
# resize the frame to 500
detector.currentFrame = imutils.resize(detector.currentFrame, width=500)
detector.framecounter += 1
if detector.framecounter > 1:
cnts = detector.prepareFrame()
for c in cnts:
bound_rect = cv2.boundingRect(c)
#(x, y, w, h) = cv2.boundingRect(c)
#initBB2 =(x,y,w,h)
prott1 = r'ML-Models/MobileNetSSD_deploy.prototxt'
prott2 = r'ML-Models/MobileNetSSD_deploy.caffemodel'
net = cv2.dnn.readNetFromCaffe(prott1, prott2)
#trackbox = detector.currentFrame[y:y+h, x:x+w]boundRect[1]
trackbox = detector.currentFrame[bound_rect[1]:bound_rect[1]+bound_rect[3],
bound_rect[0]:bound_rect[0]+bound_rect[2]]
trackbox = cv2.resize(trackbox, (224, 224))
#cv2.imshow('image',trackbox)
blob = cv2.dnn.blobFromImage(cv2.resize(trackbox, (300, 300)),0.007843, (300, 300), 127.5)
net.setInput(blob)
detections = net.forward()
for i in np.arange(0, detections.shape[2]):
people_count += detector.detectConfidentiallyPeople(i, detections, bound_rect)
cv2.rectangle(detector.currentFrame, (bound_rect[0], bound_rect[1]),
(bound_rect[0] + bound_rect[2], bound_rect[1] + bound_rect[3]), (255, 255, 0), 1)
# show the frame and record if the user presses a key
cv2.imshow("Video stream", detector.currentFrame)
key = cv2.waitKey(1) & 0xFF
# send number of people detected via mqtt
mqtt_client.publish("/gso/bb/104/Camera", str(people_count))
# if the `q` key is pressed, break from the lop
if key == ord("q"):
break
if key == ord("d"):
detector.firstFrame = None
#detector.lastFrame = detector.currentFrame
timer.print_frame_time()
# finally, stop the camera/stream and close any open windows
if args.get("video", None) is not None:
vs.stop() if args.get("video", None) is None else vs.release()
cv2.destroyAllWindows()
finally:
if args.get("video", None) is None:
image_hub.send_reply(b'OK')
class DetectionFromFrame:
def __init__(self, min_size, confidence):
self.min_size = min_size
self.confidence_level = confidence
self.firstFrame = None
self.currentFrame = None
self.initBB2 = None
self.fps = None
self.differ = None
self.now = ''
self.framecounter = 0
self.people_count_total = 0
def prepareFrame(self):
gray = cv2.cvtColor(self.currentFrame, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (21, 21), 0)
# if the first frame is None, initialize it
if self.firstFrame is None:
self.firstFrame = gray
return []
# compute the absolute difference between the current frame and first frame
frameDelta = cv2.absdiff(self.firstFrame, gray)
thresh = cv2.threshold(frameDelta, 25, 255, cv2.THRESH_BINARY)[1]
#debug
"""if VISUAL_DEBUG:
cv2.imshow("debug image", thresh)
cv2.waitKey(0)
cv2.destroyWindow("debug image")
#cv2.destroyWindow("threshhold image")"""
# dilate the thresholded image to fill in holes
thresh = cv2.dilate(thresh, None, iterations=2)
# find contours on thresholded image
thresh = np.uint8(thresh)
cnts, _ = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
return cnts
def detectConfidentiallyPeople(self, i, detections, bound_rect):
#CLASSES = ["person"]
detected_color = (0, 255, 0)
#COLORS = np.random.uniform(0, 255, size=(len(CLASSES), 3))
confidence = detections[0, 0, i, 2]
if confidence > self.confidence_level:
# draw a rectangle in green over the detected area
cv2.rectangle(self.currentFrame, (bound_rect[0], bound_rect[1]),
(bound_rect[0] + bound_rect[2], bound_rect[1] + bound_rect[3]), detected_color, 3)
label = "{:.2f}%".format(confidence * 100)
cv2.putText(self.currentFrame, label, (bound_rect[0], bound_rect[1]-5), cv2.FONT_HERSHEY_SIMPLEX, 0.3, detected_color, 1)
return 1
else:
return 0
class Timer:
def __init__(self):
self.frame_timer = None
self.contour_timer = None
self.detection_timer = None
self.contour_time = []
self.detection_time = []
def start_frame_timer(self):
self.frame_timer = time.time()
def get_frame_time(self):
return time.time() - self.frame_timer
def start_contour_timer(self):
self.contour_timer = time.time()
def stop_contour_timer(self):
self.contour_time.append(time.time() - self.contour_timer)
def start_detection_timer(self):
self.detection_timer = time.time()
def stop_detection_timer(self):
self.detection_time.append(time.time() - self.detection_timer)
def print_frame_time(self):
print("Time for Frame: {:.2f}.".format(self.get_frame_time()))
def print_other_times(self):
average_contour = 0 if not self.contour_time else sum(self.contour_time)/float(len(self.contour_time))
average_detection = 0 if not self.detection_time else sum(self.detection_time)/float(len(self.detection_time))
median_contour = 0 if not self.contour_time else median(self.contour_time)
median_detection = 0 if not self.detection_time else median(self.detection_time)
total_contour = sum(self.contour_time)
total_detection = sum(self.detection_time)
print("Contour Total: {:.2f}. Contour Median: {:.2f}. Contour Average: {:.2f}.".format(
total_contour, median_contour, average_contour))
print("Detection Total: {:.2f}. Detection Median: {:.2f}. Detection Average: {:.2f}. ".format(
total_detection, median_detection, average_detection))
self.contour_time = []
self.detection_time = []
if __name__ == "__main__":
main()
+241
View File
@@ -0,0 +1,241 @@
#!/usr/bin/env python
from imutils.video import VideoStream
from imutils.video import FPS
import argparse
import imutils
import time
import cv2
from datetime import datetime, time
import numpy as np
import time as time2
VISUAL_DEBUG=True
""" Arguments """
ap = argparse.ArgumentParser()
ap.add_argument("-v", "--video", help="path to the video file")
ap.add_argument("-a", "--min-area", type=int, default=500, help="minimum area size")
ap.add_argument("-t", "--tracker", type=str, default="csrt", help="OpenCV object tracker type")
args = vars(ap.parse_args())
""" Determine opencv version and select tracker """
# extract the OpenCV version info
(major, minor) = cv2.__version__.split(".")[:2]
# if we are using OpenCV 3.2 or an earlier version, we can use a special factory
# function to create the entity that tracks objects
if int(major) == 3 and int(minor) < 3:
tracker = cv2.Tracker_create(args["tracker"].upper())
#tracker = cv2.TrackerGOTURN_create()
# otherwise, for OpenCV 3.3 or newer,
# we need to explicity call the respective constructor that contains the tracker object:
else:
# initialize a dictionary that maps strings to their corresponding
# OpenCV object tracker implementations
OPENCV_OBJECT_TRACKERS = {
"csrt": cv2.TrackerCSRT_create,
"kcf": cv2.TrackerKCF_create,
"boosting": cv2.TrackerBoosting_create,
"mil": cv2.TrackerMIL_create,
"tld": cv2.TrackerTLD_create,
"medianflow": cv2.TrackerMedianFlow_create,
"mosse": cv2.TrackerMOSSE_create
}
# grab the appropriate object tracker using our dictionary of
# OpenCV object tracker objects
tracker = OPENCV_OBJECT_TRACKERS[args["tracker"]]()
#tracker = cv2.TrackerGOTURN_create()
# if the video argument is None, then the code will read from webcam (work in progress)
if args.get("video", None) is None:
vs = VideoStream(src=0).start()
time2.sleep(2.0)
# otherwise, we are reading from a video file
else:
vs = cv2.VideoCapture(args["video"])
"""" Analyzing video frames """
# loop over the frames of the video, and store corresponding information from each frame
firstFrame = None
initBB2 = None
fps = None
differ = None
now = ''
framecounter = 0
trackeron = 0
while True:
people_count_per_frame = 0
frame = vs.read()
frame = frame if args.get("video", None) is None else frame[1]
# if the frame can not be grabbed, then we have reached the end of the video
if frame is None:
break
# resize the frame to 500
frame = imutils.resize(frame, width=500)
framecounter = framecounter+1
if framecounter > 1:
(H, W) = frame.shape[:2]
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (21, 21), 0)
# if the first frame is None, initialize it
if firstFrame is None:
firstFrame = gray
continue
# compute the absolute difference between the current frame and first frame
frameDelta = cv2.absdiff(firstFrame, gray)
thresh = cv2.threshold(frameDelta, 25, 255, cv2.THRESH_BINARY)[1]
#debug
if VISUAL_DEBUG:
cv2.imshow("debug image", thresh)
cv2.waitKey(0)
#cv2.destroyWindow("threshhold image")
# dilate the thresholded image to fill in holes
thresh = cv2.dilate(thresh, None, iterations=2)
# find contours on thresholded image
thresh = np.uint8(thresh)
cnts, im2 = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
if VISUAL_DEBUG:
"""img = cv2.drawContours(thresh.copy(), cnts, -1, (0,255,0), 3)
cv2.imshow("debug image", img)
cv2.waitKey(0)"""
print(len(cnts))
# loop over the contours identified
contourcount = 0
for c in cnts:
contourcount = contourcount + 1
# if the contour is too small, ignore it
if cv2.contourArea(c) < args["min_area"]:
continue
# compute the bounding box for the contour, draw it on the frame,
(x, y, w, h) = cv2.boundingRect(c)
initBB2 =(x,y,w,h)
prott1 = r'ML-Models/MobileNetSSD_deploy.prototxt'
prott2 = r'ML-Models/MobileNetSSD_deploy.caffemodel'
net = cv2.dnn.readNetFromCaffe(prott1, prott2)
CLASSES = ["person"]
COLORS = np.random.uniform(0, 255, size=(len(CLASSES), 3))
trackbox = frame[y:y+h, x:x+w]
trackbox = cv2.resize(trackbox, (224, 224))
#cv2.imshow('image',trackbox)
"""if VISUAL_DEBUG:
trackbox2 = thresh[y:y+h, x:x+w]
trackbox2 = cv2.resize(trackbox2, (224, 224))
cv2.imshow('debug image',trackbox2)
cv2.waitKey(0)"""
blob = cv2.dnn.blobFromImage(cv2.resize(trackbox, (300, 300)),0.007843, (300, 300), 127.5)
net.setInput(blob)
detections = net.forward()
for i in np.arange(0, detections.shape[2]):
confidence = detections[0, 0, i, 2]
confidence_level = 0.95
if confidence > confidence_level:
people_count_per_frame+=1
people_count_total+=1
# extract the index of the class label from the `detections`, then compute the (x, y)-coordinates of
# the bounding box for the object
idx = int(detections[0, 0, i, 1])
box = detections[0, 0, i, 3:7] * np.array([w, h, w, h])
(startX, startY, endX, endY) = box.astype("int")
# draw the prediction on the frame
#label = "{}: {:.2f}%".format(CLASSES[idx], confidence * 100)
label = "{}: {:.2f}%".format(CLASSES[0], confidence * 100)
#cv2.rectangle(frame, (startX, startY), (endX, endY), COLORS[idx], 2)
cv2.rectangle(frame, (startX, startY), (endX, endY), COLORS[0], 2)
y = startY - 15 if startY - 15 > 15 else startY + 15
#cv2.putText(frame, label, (startX, y), cv2.FONT_HERSHEY_SIMPLEX, 0.5, COLORS[idx], 2)
cv2.putText(frame, label, (startX, y), cv2.FONT_HERSHEY_SIMPLEX, 0.5, COLORS[0], 2)
if VISUAL_DEBUG:
print("person found")
cv2.imshow("debug image", frame)
key = cv2.waitKey(0)
cv2.rectangle(frame, (x, y), (x + w, y + h), (255, 255, 0), 2)
# Start tracker
now = datetime.now()
if differ == None or differ > 9:
tracker.init(frame, initBB2)
fps = FPS().start()
# check to see if we are currently tracking an object, if so, ignore other boxes
# this code is relevant if we want to identify particular persons
"""if initBB2 is not None:
# grab the new bounding box coordinates of the object
(success, box) = tracker.update(frame)
# check to see if the tracking was a success
differ = 10
if success:
(x, y, w, h) = [int(v) for v in box]
cv2.rectangle(frame, (x, y), (x + w, y + h),(0, 255, 0), 2)
differ = abs(initBB2[0]-box[0]) + abs(initBB2[1]-box[1])
i = tracker.update(lastframe)
if i[0] != True:
time2.sleep(4000)
else:
trackeron = 1
# update the FPS counter
fps.update()
fps.stop()
# initialize the set of information we'll be displaying on
# the frame
info = [
("Success", "Yes" if success else "No"),
("FPS", "{:.2f}".format(fps.fps())),
("People Frame", "{}".format(people_count_per_frame)),
("People Total", "{}".format(people_count_total))
]
# loop over the info tuples and draw them on our frame
for (i, (k, v)) in enumerate(info):
text = "{}: {}".format(k, v)
cv2.putText(frame, text, (10, H - ((i * 20) + 20)), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2)
# draw the text and timestamp on the frame
now2 = datetime.now()
time_passed_seconds = str((now2-now).seconds)
cv2.putText(frame, 'Detecting persons',(10, 20), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2)"""
# show the frame and record if the user presses a key
cv2.imshow("Video stream", frame)
key = cv2.waitKey(1) & 0xFF
# if the `q` key is pressed, break from the lop
if key == ord("q"):
break
if key == ord("d"):
firstFrame = None
lastframe = frame
# finally, stop the camera/stream and close any open windows
vs.stop() if args.get("video", None) is None else vs.release()
cv2.destroyAllWindows()
+232
View File
@@ -0,0 +1,232 @@
""" imagezmq: Transport OpenCV images via ZMQ.
Classes that transport OpenCV images from one computer to another. For example,
OpenCV images gathered by a Raspberry Pi camera could be sent to another
computer for displaying the images using cv2.imshow() or for further image
processing. See API and Usage Examples for details.
Copyright (c) 2017 by Jeff Bass.
License: MIT, see LICENSE for more details.
"""
import zmq
import numpy as np
import cv2
class ImageSender():
"""Opens zmq REQ socket and sends images.
Opens a zmq REQ socket on the image sending computer, often a
Raspberry Pi, that will be sending OpenCV images and
related text messages to the hub computer. Provides methods to
send images or send jpg compressed images.
Arguments:
connect_to: the tcp address:port of the hub computer.
"""
def __init__(self, connect_to='tcp://127.0.0.1:63128'):
"""Initializes zmq socket for sending images to the hub.
Expects an open socket at the connect_to tcp address; it will
connect to that remote socket after setting up the REQ
socket on this computer.
"""
self.zmq_context = SerializingContext()
self.zmq_socket = self.zmq_context.socket(zmq.REQ)
self.zmq_socket.connect(connect_to)
def send_image(self, msg, image):
"""Sends OpenCV image and msg to hub computer.
Arguments:
msg: text message or image name.
image: OpenCV image to send to hub.
Returns:
A text reply from hub.
"""
if image.flags['C_CONTIGUOUS']:
# if image is already contiguous in memory just send it
self.zmq_socket.send_array(image, msg, copy=False)
else:
# else make it contiguous before sending
image = np.ascontiguousarray(image)
self.zmq_socket.send_array(image, msg, copy=False)
hub_reply = self.zmq_socket.recv() # receive the reply message
return hub_reply
def send_jpg(self, msg, jpg_buffer):
"""Sends msg text and jpg buffer to hub computer.
Arguments:
msg: image name or message text.
jpg_buffer: bytestring containing the jpg image to send to hub.
Returns:
A text reply from hub.
"""
self.zmq_socket.send_jpg(msg, jpg_buffer, copy=False)
hub_reply = self.zmq_socket.recv() # receive the reply message
return hub_reply
class ImageHub():
"""Opens zmq REP socket and receives images.
Opens a zmq REP socket on the hub compuer, for example,
a Mac, that will be receiving and displaying or processing OpenCV images
and related text messages. Provides methods to receive images or receive
jpg compressed images.
Arguments:
open_port: (optional) the socket to open for receiving REQ requests.
"""
def __init__(self, open_port='tcp://*:63128'):
"""Initializes zmq REP socket to receive images and text.
"""
self.zmq_context = SerializingContext()
self.zmq_socket = self.zmq_context.socket(zmq.REP)
self.zmq_socket.bind(open_port)
def recv_image(self, copy=False):
"""Receives OpenCV image and text msg.
Arguments:
copy: (optional) zmq copy flag.
Returns:
msg: text msg, often the image name.
image: OpenCV image.
"""
msg, image = self.zmq_socket.recv_array(copy=False)
return msg, image
def recv_jpg(self, copy=False):
"""Receives text msg, jpg buffer.
Arguments:
copy: (optional) zmq copy flag
Returns:
msg: text message, often image name
jpg_buffer: bytestring jpg compressed image
"""
msg, jpg_buffer = self.zmq_socket.recv_jpg(copy=False)
return msg, jpg_buffer
def send_reply(self, reply_message=b'OK'):
"""Sends the zmq REP reply message.
Arguments:
reply_message: reply message text, often just string 'OK'
"""
self.zmq_socket.send(reply_message)
class SerializingSocket(zmq.Socket):
"""Numpy array serialization methods.
Modelled on PyZMQ serialization examples.
Used for sending / receiving OpenCV images, which are Numpy arrays.
Also used for sending / receiving jpg compressed OpenCV images.
"""
def send_array(self, A, msg='NoName', flags=0, copy=True, track=False):
"""Sends a numpy array with metadata and text message.
Sends a numpy array with the metadata necessary for reconstructing
the array (dtype,shape). Also sends a text msg, often the array or
image name.
Arguments:
A: numpy array or OpenCV image.
msg: (optional) array name, image name or text message.
flags: (optional) zmq flags.
copy: (optional) zmq copy flag.
track: (optional) zmq track flag.
"""
md = dict(
msg=msg,
dtype=str(A.dtype),
shape=A.shape,
)
self.send_json(md, flags | zmq.SNDMORE)
return self.send(A, flags, copy=copy, track=track)
def send_jpg(self,
msg='NoName',
jpg_buffer=b'00',
flags=0,
copy=True,
track=False):
"""Send a jpg buffer with a text message.
Sends a jpg bytestring of an OpenCV image.
Also sends text msg, often the image name.
Arguments:
msg: image name or text message.
jpg_buffer: jpg buffer of compressed image to be sent.
flags: (optional) zmq flags.
copy: (optional) zmq copy flag.
track: (optional) zmq track flag.
"""
md = dict(msg=msg, )
self.send_json(md, flags | zmq.SNDMORE)
return self.send(jpg_buffer, flags, copy=copy, track=track)
def recv_array(self, flags=0, copy=True, track=False):
"""Receives a numpy array with metadata and text message.
Receives a numpy array with the metadata necessary
for reconstructing the array (dtype,shape).
Returns the array and a text msg, often the array or image name.
Arguments:
flags: (optional) zmq flags.
copy: (optional) zmq copy flag.
track: (optional) zmq track flag.
Returns:
msg: image name or text message.
A: numpy array or OpenCV image reconstructed with dtype and shape.
"""
md = self.recv_json(flags=flags)
msg = self.recv(flags=flags, copy=copy, track=track)
A = np.frombuffer(msg, dtype=md['dtype'])
return (md['msg'], A.reshape(md['shape']))
def recv_jpg(self, flags=0, copy=True, track=False):
"""Receives a jpg buffer and a text msg.
Receives a jpg bytestring of an OpenCV image.
Also receives a text msg, often the image name.
Arguments:
flags: (optional) zmq flags.
copy: (optional) zmq copy flag.
track: (optional) zmq track flag.
Returns:
msg: image name or text message.
jpg_buffer: bytestring, containing jpg image.
"""
md = self.recv_json(flags=flags) # metadata text
jpg_buffer = self.recv(flags=flags, copy=copy, track=track)
return (md['msg'], jpg_buffer)
class SerializingContext(zmq.Context):
_socket_class = SerializingSocket
+45
View File
@@ -0,0 +1,45 @@
import io
import socket
import struct
import time
import picamera
# Connect a client socket to my_server:8000 (change my_server to the
# hostname of your server)
client_socket = socket.socket()
client_socket.connect(('my_server', 8000))
# Make a file-like object out of the connection
connection = client_socket.makefile('wb')
try:
with picamera.PiCamera() as camera:
camera.resolution = (640, 480)
# Start a preview and let the camera warm up for 2 seconds
camera.start_preview()
time.sleep(2)
# Note the start time and construct a stream to hold image data
# temporarily (we could write it directly to connection but in this
# case we want to find out the size of each capture first to keep
# our protocol simple)
start = time.time()
stream = io.BytesIO()
for foo in camera.capture_continuous(stream, 'jpeg'):
# Write the length of the capture to the stream and flush to
# ensure it actually gets sent
connection.write(struct.pack('<L', stream.tell()))
connection.flush()
# Rewind the stream and send the image data over the wire
stream.seek(0)
connection.write(stream.read())
# If we've been capturing for more than 30 seconds, quit
if time.time() - start > 30:
break
# Reset the stream for the next capture
stream.seek(0)
stream.truncate()
# Write a length of zero to the stream to signal we're done
connection.write(struct.pack('<L', 0))
finally:
connection.close()
client_socket.close()
@@ -0,0 +1,47 @@
import io
import socket
import struct
import time
import cv2
# Connect a client socket to my_server:8000 (change my_server to the
# hostname of your server)
client_socket = socket.socket()
client_socket.connect(('192.168.1.107', 8000))
# Make a file-like object out of the connection
connection = client_socket.makefile('wb')
try:
cap = cv2.VideoCapture("run.mp4")
while True:
ret, frame = cap.read()
if frame is None:
break
print(type(frame))
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
print(type(gray))
_, image = cv2.imencode('.jpg', gray)
print(type(image))
stream = image.tobytes()
print(type(stream))
# Write the length of the capture to the stream and flush to
# ensure it actually gets sent
connection.write(struct.pack('<L', stream.tell()))
connection.flush()
# Rewind the stream and send the image data over the wire
stream.seek(0)
connection.write(stream.read())
# If we've been capturing for more than 30 seconds, quit
if time.time() - start > 30:
break
# Reset the stream for the next capture
stream.seek(0)
stream.truncate()
# Write a length of zero to the stream to signal we're done
connection.write(struct.pack('<L', 0))
finally:
connection.close()
client_socket.close()
+34
View File
@@ -0,0 +1,34 @@
import io
import socket
import struct
from PIL import Image
# Start a socket listening for connections on 0.0.0.0:8000 (0.0.0.0 means
# all interfaces)
server_socket = socket.socket()
server_socket.bind(('0.0.0.0', 8000))
server_socket.listen(0)
# Accept a single connection and make a file-like object out of it
connection = server_socket.accept()[0].makefile('rb')
try:
while True:
# Read the length of the image as a 32-bit unsigned int. If the
# length is zero, quit the loop
image_len = struct.unpack('<L', connection.read(struct.calcsize('<L')))[0]
if not image_len:
break
# Construct a stream to hold the image data and read the image
# data from the connection
image_stream = io.BytesIO()
image_stream.write(connection.read(image_len))
# Rewind the stream, open it as an image with PIL and do some
# processing on it
image_stream.seek(0)
image = Image.open(image_stream)
print('Image is %dx%d' % image.size)
image.verify()
print('Image is verified')
finally:
connection.close()
server_socket.close()
+23
View File
@@ -0,0 +1,23 @@
from imutils.video import VideoStream
import imagezmq
import argparse
import socket
import time
# construct the argument parser and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-s", "--server-ip", required=True,
help="ip address of the server to which the client will connect")
args = vars(ap.parse_args())
# initialize the ImageSender object with the socket address of the
# server
sender = imagezmq.ImageSender(connect_to="tcp://{}:63128".format(
args["server_ip"]))
rpi_name = socket.gethostname() # send RPi hostname with each image
picam = VideoStream(usePiCamera=True).start()
time.sleep(2.0) # allow camera sensor to warm up
while True: # send images as stream until Ctrl-C
image = picam.read()
sender.send_image(rpi_name, image)
@@ -0,0 +1,26 @@
from imutils.video import FileVideoStream
import imagezmq
import argparse
import socket
import time
# construct the argument parser and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-s", "--server-ip", required=True,
help="ip address of the server to which the client will connect")
args = vars(ap.parse_args())
# initialize the ImageSender object with the socket address of the
# server
sender = imagezmq.ImageSender(connect_to="tcp://{}:5555".format(
args["server_ip"]))
rpi_name = socket.gethostname() # send RPi hostname with each image
video_file = FileVideoStream("run.mp4").start()
time.sleep(2.0) # allow camera sensor to warm up
while True:
image = video_file.read()
if image is None:
break
sender.send_image(rpi_name, image)
+11
View File
@@ -0,0 +1,11 @@
import cv2
import imagezmq
image_hub = imagezmq.ImageHub()
while True: # show streamed images until Ctrl-C
rpi_name, image = image_hub.recv_image()
cv2.imshow(rpi_name, image) # 1 window for each RPi
cv2.waitKey(1)
image_hub.send_reply(b'OK')
-1
View File
@@ -1 +0,0 @@
Git test