(WS19-01)Wetterballon Entwicklung: Unterschied zwischen den Versionen

Aus Verteilte Systeme - Wiki
Wechseln zu: Navigation, Suche
(Auszüge aus dem Quellcode)
K (Real-Time-Clock)
 
(2 dazwischenliegende Versionen von 2 Benutzern werden nicht angezeigt)
Zeile 33: Zeile 33:
 
Nach der Abwägung der Vor- und Nachteile haben wir uns für eine Implementierung mittels der bcm2835-Bibliothek entschieden, da uns die bessere Performance den größten Vorteil bei der Erfüllung der Anforderungen bot, auch wenn die Benutzerfreundlichkeit darunter litt.
 
Nach der Abwägung der Vor- und Nachteile haben wir uns für eine Implementierung mittels der bcm2835-Bibliothek entschieden, da uns die bessere Performance den größten Vorteil bei der Erfüllung der Anforderungen bot, auch wenn die Benutzerfreundlichkeit darunter litt.
  
== Quellcode Master & Tasks ==
+
== Auszüge aus dem Quellcode ==
Nach dem in der Designphase (Link) festgeleten Mustern wurde der Master, die Sensoren sowie die Tasks wie folgt implementiert:
+
Nach dem in der [https://wwwvs.cs.hs-rm.de/vs-wiki/index.php/(WS19-01)Wetterballon_Design Designphase] festgeleten Mustern wurde der Master, die Sensoren sowie die Tasks wie folgt implementiert:
 
<div class="mw-collapsible mw-collapsed" style="width:100%">
 
<div class="mw-collapsible mw-collapsed" style="width:100%">
 
;master.h
 
;master.h
Zeile 1.267: Zeile 1.267:
 
</div>
 
</div>
  
== Auszüge aus dem Quellcode ==
 
Nach dem in der [https://wwwvs.cs.hs-rm.de/vs-wiki/index.php/(WS19-01)Wetterballon_Design Designphase] festgeleten Mustern wurde der Master, die Sensoren sowie die Tasks wie folgt implementiert:
 
 
<div class="mw-collapsible mw-collapsed" style="width:100%">
 
<div class="mw-collapsible mw-collapsed" style="width:100%">
;master.h
+
; definitions.h  
 
<div class="mw-collapsible-content">
 
<div class="mw-collapsible-content">
<syntaxhighlight lang = "c">
+
<syntaxhighlight lang="c">
 
/**
 
/**
  * @file  master.h
+
  * @file  definitions.h
  * @brief Definitions for the Master program.
+
  * @brief Definitions used by multiple header files
 
  *
 
  *
  * @author Jonas Gaida, Florian Schwarz
+
  * @author Florian Schwarz
 
  */
 
  */
  
#ifndef INCLUDE_MASTER_H_
+
#ifndef INCLUDE_DEFINITIONS_H_
#define INCLUDE_MASTER_H_
+
#define INCLUDE_DEFINITIONS_H_
 
 
#include "sensor.h"
 
 
 
#include "sensor_gyml8511.h"
 
#include "sensor_mcp9808.h"
 
#include "sensor_mpu9250.h"
 
#include "sensor_ms5611_out.h"
 
#include "sensor_ms8607_out.h"
 
#include "sensor_ms8607_in.h"
 
#include "sensor_sht31d_out.h"
 
#include "sensor_sht35_out.h"
 
#include "sensor_sht35_in.h"
 
#include "sensor_tmp117.h"
 
#include "sensor_camm8.h"
 
 
 
#include "sensor_height_sim.h" // Simulates flight height data
 
 
 
#include "sensor_buzzer.h"
 
 
 
#include "camera_raspicam.h"
 
#include "camera_usb.h"
 
  
  
 
/**
 
/**
  * @brief Array of all registered sensors.
+
  * @brief Possible operating phases of weather balloon as bit mask in chronological
 +
*        order.
 
  *
 
  *
  * This needs to be manually filled.
+
  * Combine masks with bitwise OR ("|") and check for phase with bitwise AND
 +
* ("&"). The result of this is automatically usable as boolean value.\n
 +
* \n
 +
* Keeping a chronological order is important for being able to switch through
 +
* the phases by just bit-shifting the active phase.
 
  */
 
  */
const i2c_sensor_reg SENSORS[] = {
+
typedef enum phase {
MS8607_OUT,
+
START    = 0x01, ///< Pre-flight phase.
MS5611_OUT,
+
ASCEND  = 0x02, ///< Ascending phase.
MS8607_IN,
+
DESCEND  = 0x04, ///< Descending phase.
SHT35_OUT,
+
RECOVERY = 0x08, ///< Recovery phase.
SHT31D_OUT,
+
 
SHT35_IN,
+
// Special phases.
TMP117,
+
QUIT    = 0x10,   ///< Not an actual phase. Triggers program termination.
MCP9808,
 
MPU9250,
 
GYML8511,
 
CAMM8,
 
HEIGHT_SIM,
 
BUZZER
 
};
 
  
 +
// Combined phases.
 +
FLIGHT  = 0x06    ///< ASCEND | DESCEND
 +
} phase;
  
/**
+
#endif // INCLUDE_DEFINITIONS_H_
* @brief Constant for easy access to the total number of registered sensors.
 
  */
 
const size_t SENSOR_AMT = sizeof SENSORS / sizeof (i2c_sensor_reg);
 
  
  
/**
 
* @brief Array of all cameras.
 
*
 
* This needs to be manually filled.
 
*/
 
const camera CAMERAS[] = {
 
RASPICAM,
 
USBCAM
 
};
 
  
 +
</syntaxhighlight>
 +
</div>
 +
</div>
 +
<div class="mw-collapsible mw-collapsed" style="width:100%">
 +
; measurement.h
 +
<div class="mw-collapsible-content">
 +
<syntaxhighlight lang="c">
  
 
/**
 
/**
  * @brief Constant for easy access to the total number of cameras.
+
* @file  measurement.h
 +
  * @brief Definitions for the Master programm.
 +
*
 +
* @author Florian Schwarz, Jonas Gaida
 
  */
 
  */
const size_t CAMERA_AMT = sizeof CAMERAS / sizeof (camera);
 
  
 +
#ifndef INCLUDE_MEASUREMENTDATA_H_
 +
#define INCLUDE_MEASUREMENTDATA_H_
  
/**
+
#include <stddef.h>  // size_t
* @brief Path of the persistence file for the phase.
+
#include <time.h> // struct timespec
  */
 
const char *const PERSISTENCE_PATH = "./phase";
 
  
  
  
 
/**
 
/**
  * @brief The phase in which the master currently is.
+
  * @brief Possible types of data stored in a measurement struct data field.
 +
*
 +
* @sa measurement
 
  */
 
  */
extern phase active_phase;
+
typedef enum date_type {
 
+
EXOTIC,            ///< Something else.
 
+
HEIGHT,            ///< Height in m (meter).
 +
PRESSURE,          ///< Atmospheric pressure in hPa (hectopascal).
 +
TEMPERATURE,        ///< Temperature in °C (degree Celsius).
 +
HUMIDITY,          ///< Relative humidity in % (percent).
 +
ACCELERATION_X,    ///< Acceleration in m/s^2 (meter per second squared).
 +
ACCELERATION_Y,    ///< Acceleration in m/s^2 (meter per second squared).
 +
ACCELERATION_Z,    ///< Acceleration in m/s^2 (meter per second squared).
 +
ROTATION_X,        ///< Rotation in °/s (degree per second).
 +
ROTATION_Y,        ///< Rotation in °/s (degree per second).
 +
ROTATION_Z,        ///< Rotation in °/s (degree per second).
 +
MAGNETIC_FLUX_X,    ///< Magnetic flux density in T (Tesla).
 +
MAGNETIC_FLUX_Y,    ///< Magnetic flux density in T (Tesla).
 +
MAGNETIC_FLUX_Z,    ///< Magnetic flux density in T (Tesla).
 +
IRRADIANCE,        ///< Irradiance of UV radiation in W/m^2 (Watt per square meter).
 +
LUM_INTENSITY,      ///< Intensity of visible light in cd (candela).
 +
POS_LAT,            ///< Latitude in DD (decimal degree).
 +
POS_LON            ///< Longitute in DD (decimal degree).
 +
} date_type;
  
int main(void);
+
/**
 +
* @brief Interpreted measurements of a sensor of a specified kind.
 +
*/
 +
typedef struct measurement {
 +
struct timespec time;        ///< Unix time when the data was recorded.
 +
size_t          data_c;      ///< How many data fields exist.
 +
date_type      *types;      ///< Which kind of data is stored in each field.
 +
signed char    *priorities; ///< Priority for each field. May be used to select most accurat sensor for master, e.g. height determination. 0 default, the higher, the more prioritized
 +
double          *data;      ///< Array of data fields.
 +
} measurement;
  
  
// Signal handler functions
 
  
 
/**
 
/**
  * @brief Reads the phase saved in the file at PERSISTENCE_PATH.
+
  * @brief Creates data array by allocating uninitialized memory and sets
 +
*        attributes. Priorities are set to 0.
 
  *
 
  *
  * This method only reads if the file exists. Otherwise it does not create it
+
  * If @p data_c is zero, the method returns successfully and @c types and
  * with a default phase. Instead it just returns unsuccessfully.
+
  * @c data are set to @c NULL. This is for other devices posing as sensors.
 
  *
 
  *
  * @param[out] p Pointer to a phase which will be filled.
+
  * @param[out] m      Pointer to an existing measurement struct.
 +
* @param[in]  data_c Number of data fields.
 +
* @param[in]  types  Types of data stored in @c m->data. Length is @p data_c.
 +
* @param[in]  priorities Priorities for each measurement @c m->data. Length is @p data_c. If Null, all priorities will get set to 0.
 
  *
 
  *
 
  * @return @c EXIT_SUCCESS if successful else @c EXIT_FAILURE.
 
  * @return @c EXIT_SUCCESS if successful else @c EXIT_FAILURE.
 
  *
 
  *
  * @sa PERSISTENCE_PATH
+
  * @sa measurement_destroy
 
  */
 
  */
int read_phase(phase *const p);
+
int measurement_create(measurement *const m, size_t data_c, const date_type *const types, const signed char *const priorities);
 
 
  
 
/**
 
/**
  * @brief Writes the given phase to the file at PERSISTENCE_PATH.
+
  * @brief Destroys data array by freeing memory and resets all attributes.
 +
*
 +
* If @c m.data_c is zero and @c m.data is @c NULL, nothing happens and the
 +
* method returns successfully.
 
  *
 
  *
  * @param[in] p The phase to be written.
+
  * @param[out] m Pointer to an existing measurement struct.
 
  *
 
  *
 
  * @return @c EXIT_SUCCESS if successful else @c EXIT_FAILURE.
 
  * @return @c EXIT_SUCCESS if successful else @c EXIT_FAILURE.
 
  *
 
  *
  * @sa PERSISTENCE_PATH
+
  * @sa measurement_create
 
  */
 
  */
int write_phase(phase p);
+
int measurement_destroy(measurement *const m);
 
 
  
 
/**
 
/**
  * @brief Checks the health running master.
+
  * @brief Append a measurement as line in CSV format to an open, writable file.
 
  *
 
  *
  * Implements the function type required by a signal handler.
+
  * @param[in] fd  Open file descriptor with write permission.
 +
* @param[in] m The measurement struct to be written.
 
  *
 
  *
  * @param[in] sig The signal which triggered the handler call.
+
  * @return @c EXIT_SUCCESS if successful else @c EXIT_FAILURE.
 
  */
 
  */
void sh_maintenance(int sig);
+
int measurement_append_to_csv_file(int fd, measurement m);
  
  
 
/**
 
/**
  * @brief Forces the active phase to be re-read.
+
  * @brief Prints info about measurement struct
 
  *
 
  *
* Implements the function type required by a signal handler.
+
  * @param[in] m measurement struct to print
*
 
  * @param[in] sig The signal which triggered the handler call.
 
*
 
* @sa read_phase, active_phase
 
 
  */
 
  */
void sh_update_phase(int sig);
+
void measurement_print(measurement m);
  
  
/**
+
#endif  // INCLUDE_MEASUREMENTDATA_H_
* @brief Contains logic for the FALLBACK_STATE_TO_RECOVERY_CHANGER task
+
 
*
 
* Ensures that master switches to RECOVERY phase after MAX_FLIGHT_TIME.
 
* To withstand forced reboot, this task writes its time for phase change to a file specified by MAX_FLIGHT_TIME_PATH
 
*
 
* @param[in,out] thiz pointer to own task struct
 
* @param[in,out] cur_phase pointer to current active phase
 
* @param[in] cur_time current time
 
*
 
*/
 
int fallback_recovery_task(task *thiz, phase *cur_phase, struct timespec cur_time);
 
#endif  // INCLUDE_MASTER_H_
 
 
 
 
</syntaxhighlight>
 
</syntaxhighlight>
 
</div>
 
</div>
 
</div>
 
</div>
 
 
<div class="mw-collapsible mw-collapsed" style="width:100%">
 
<div class="mw-collapsible mw-collapsed" style="width:100%">
;master.c
+
; measurement.c
 
<div class="mw-collapsible-content">
 
<div class="mw-collapsible-content">
<syntaxhighlight lang = "c">
+
<syntaxhighlight lang="c">
 +
 
 
/**
 
/**
  * @file  master.c
+
  * @file  measurement.c
  * @brief Implementation of master.h
+
  * @brief Implementation of measurement.h
 
  *
 
  *
 
  * @author Jonas Gaida, Florian Schwarz
 
  * @author Jonas Gaida, Florian Schwarz
 
  */
 
  */
  
#include "master.h"
+
#include "measurement.h"
  
#include <stdlib.h>  // free, exit codes
+
#include <stdlib.h>  // exit codes
#include <unistd.h> // calloc
+
#include <unistd.h> // write
#include <stdio.h> // fprintf, fflush, stdout, stderr
+
#include <stdio.h> // fprintf
#include <fcntl.h>  // open
+
#include <string.h>  // strerror, strlen, memcpy
#include <sys/stat.h>  // open flags
+
#include <fcntl.h>  // fcntl
#include <signal.h>  // struct sigaction, sigaction
 
#include <string.h>  // strerror, strlen
 
#include <time.h>  // clock_gettime
 
#include <limits.h>  // INT_MAX
 
 
#include <errno.h>  // errno
 
#include <errno.h>  // errno
  
#include "bcm2835.h"
+
 
#include "timespec_helper.h"
+
 
#include "task.h"
+
/*
#include "task_led.h"
+
* Local definitions of CSV symbols.
#include "task_gsm.h"
+
*/
#include "task_watchdog.h"
+
static const char TIME_SEP = '.';
 +
static const char CSV_SEP = ';';
 +
static const char CSV_NEWLINE = '\n';
  
  
  
 
/**
 
/**
  * @brief Signal which triggers the sh_maintenance function.
+
  * @brief Local helper function for converting a measurement to a CSV line.
 +
*
 +
* Does not validate arguments!
 +
*
 +
* @param[in] m The measurement to be converted.
 +
*
 +
* @return Newly allocated memory containing the line as null-terminated string.
 
  *
 
  *
  * @sa sh_maintenance
+
  * @sa measurement_append_csv_to_file
 
  */
 
  */
static const int MAINTENANCE_SIGNAL = SIGUSR1;
+
static char *measurement_to_csv(measurement m) {
 +
int tmp_length = 0;
 +
 
 +
size_t line_length = 1;  // Always has a null byte.
 +
size_t pos = 0;
 +
char *line = NULL;
  
/**
+
int status = EXIT_SUCCESS;
* @brief Signal which triggers the interaction function.
 
*
 
* @sa interaction
 
*/
 
static const int UPDATE_SIGNAL = SIGUSR2;
 
  
  
/**
 
* @brief Maximum time in s until state change to RECOVERY
 
*
 
* Fallback mechanism, to guarantee final RECOVERY mode.
 
*/
 
static const unsigned long MAX_FLIGHT_TIME_S = 3 * 60 * 60; // 3 hours
 
/**
 
* @brief Stores time for phase change to RECOVERY
 
*/
 
const char *const MAX_FLIGHT_TIME_PATH = "./max_flight_time.pers";
 
  
/**
+
// Determine the length of the time as string.
* @brief Length of the ID suffix of each sensor's file name.
+
tmp_length = snprintf(NULL, 0, "%li%c%09li", m.time.tv_sec, TIME_SEP, m.time.tv_nsec);
*
+
if (0 > tmp_length) {
* This is used in a @c printf call as a '*' length field. These expect @c int
+
fprintf(stderr, "%s:%i: determining time length failed\n", __FILE__, __LINE__);
* values. Therefor the value of this constant has to stay within @c int
+
status = EXIT_FAILURE;
* boundaries.
+
goto ERR;
*/
+
}
static const size_t FORMAT_FILE_ID_LENGTH = 3;
+
line_length += (size_t) tmp_length;
  
/**
+
for (size_t i = 0; m.data_c > i; ++i) {
* @brief Number of milliseconds to wait after a failed init/measurement
+
tmp_length = snprintf(NULL, 0, "%lf", m.data[i]);
*/
+
if (0 > tmp_length) {
static const unsigned long ERR_SEQ_DELAY_MS = 50;
+
fprintf(stderr, "%s:%i: determining data length failed: index %zu\n", __FILE__, __LINE__, i);
 +
status = EXIT_FAILURE;
 +
goto ERR;
 +
}
  
/**
+
// Accumulate field lengths and add space for a CSV_SEP.
* @brief Number of milliseconds which each reconnection attempt to an sensor takes
+
line_length += 1 + (size_t) tmp_length;
*/
+
}
static const unsigned long ERR_PERIOD_MS = 3000;
 
  
/**
+
// Add space for the final CSV_NEWLINE.
* @brief Threshold for how many sequenced errors until sensors transition to failure state
+
++line_length;
*/
 
static const unsigned char ERR_THRESHOLD = 3;
 
  
/**
 
* @brief Struct for connected sensors
 
*/
 
static task *tasks = NULL;
 
  
/**
 
* @brief Struct for connected sensors
 
*/
 
static size_t tasks_amt = 0;
 
  
/**
+
// Allocate memory for the line buffer.
* @brief Struct for connected sensors
+
line = calloc(line_length, sizeof (char));
*/
+
if (NULL == line) {
static i2c_sensor *s_con = NULL;
+
fprintf(stderr, "%s:%i: calloc of %zu bytes failed\n", __FILE__, __LINE__, line_length * sizeof (char));
 +
status = EXIT_FAILURE;
 +
goto ERR;
 +
}
  
/**
 
* @brief Amount of actually connected sensors
 
*/
 
static size_t s_con_amt = 0;
 
  
  
// Defined as "extern" in master.h.
+
// Print time first.
phase active_phase;
+
tmp_length = snprintf(line, line_length, "%li%c%09li", m.time.tv_sec, TIME_SEP, m.time.tv_nsec);
 +
if (0 > tmp_length) {
 +
fprintf(stderr, "%s:%i: buffering time failed\n", __FILE__, __LINE__);
 +
status = EXIT_FAILURE;
 +
goto ERR_FREE;
 +
}
 +
pos = (size_t) tmp_length;
  
 +
// Print each data point preceeded by a CSV_SEP.
 +
for (size_t i = 0; m.data_c > i; ++i) {
 +
tmp_length = snprintf(&line[pos], line_length - pos, "%c%f", CSV_SEP, m.data[i]);
 +
if (0 > tmp_length) {
 +
fprintf(stderr, "%s:%i: buffering data failed: index %zu\n", __FILE__, __LINE__, i);
 +
status = EXIT_FAILURE;
 +
goto ERR_FREE;
 +
}
 +
pos += (size_t) tmp_length;
 +
}
  
int main(void) {
+
// Print final CSV_NEWLINE and closing null byte.
int rv = EXIT_SUCCESS; // return value for final exit
+
tmp_length = snprintf(&line[pos], line_length - pos, "%c", CSV_NEWLINE);
// int rv_f; // return value of functions.
+
if (0 > tmp_length) {
// int rv_l;  // return value for local sections
+
fprintf(stderr, "%s:%i: buffering new line failed\n", __FILE__, __LINE__);
 +
status = EXIT_FAILURE;
 +
goto ERR_FREE;
 +
}
  
uid_t uid = 0;  // For root check.
+
ERR_FREE:
baudrate baudrate = MAX_BAUDRATE;  // Should be highest by master supported I2C-frequency
+
if (EXIT_FAILURE == status) {
task *task = NULL;
+
free(line);
i2c_sensor *sensor = NULL, *sensor_height = NULL, *sensor_gps = NULL;
+
line = NULL;
size_t sensor_name_length = 0;
+
}
size_t file_name_length = 0;
 
char *file_name = NULL;
 
ssize_t written = 0;  // For write() calls.
 
struct timespec cur_time, diff_time;
 
  
struct sigaction maintenance_action_default;
+
ERR:
struct sigaction update_action_default;
+
return line;
struct sigaction maintenance_action;
+
}
struct sigaction update_action;
 
sigset_t all;
 
// TODO-DEL - Signal-Blocking while sensors running not useful in case of program freezing
 
// sigset_t normal;
 
  
const long WATCHDOG_KEEPALIVE_MS = 3000;
 
const char *const WATCHPUPPY_SIGNALING_CMD = "killall -SIGUSR1 watchpuppy.sh";
 
const long WATCHPUPPY_SIGNALING_CYCLE_S = 25; // needs to be smaller than TIMETOWAKEUP in watchpuppy.sh
 
  
const double CYCLE_LEN_MULT = 1.0; // To globaly manipulate cycle_len
+
int measurement_create(measurement *const m, size_t data_c, const date_type *const types, const signed char *const priorities) {
 +
int rv = EXIT_SUCCESS;
 +
 +
if (NULL == m) {
 +
fprintf(stderr, "%s:%i: no measurement given\n", __FILE__, __LINE__);
 +
return EXIT_FAILURE;
 +
}
 +
if (NULL == types && data_c > 0) {
 +
fprintf(stderr, "%s:%i: Input types is Null\n", __FILE__, __LINE__);
 +
return EXIT_FAILURE;
 +
}
 +
if (NULL == priorities && data_c > 0) {
 +
fprintf(stderr, "%s:%i: Input priorities is Null\n", __FILE__, __LINE__);
 +
return EXIT_FAILURE;
 +
}
  
// Constants for flight phase evaluation by height differences calculated by air pressure
+
m->types = calloc(data_c, sizeof (date_type));
 
+
if (NULL == m->types && data_c > 0) {
// Rough false value filtering
+
fprintf(stderr, "%s:%i: calloc of %zu bytes failed\n", __FILE__, __LINE__, data_c * sizeof (date_type));
const double HEIGHT_MIN = -500.0; // coast of dead sea
+
return EXIT_FAILURE;
const double HEIGHT_MAX = 50000.0; // balloon ascension limit + some puffer due to height calculation formular error in higher heights
+
}
 
+
m->priorities = calloc(data_c, sizeof (signed char));
const unsigned char HEIGHT_AMT = 10; // Amount to collect, before calculate average vertical speed, false values included
+
if (NULL == m->priorities && data_c > 0) {
const double V_VELO_HALT_TO_FLIGHT_THRESHOLD = 0.5; // Threshold in m/s
+
fprintf(stderr, "%s:%i: calloc of %zu bytes failed\n", __FILE__, __LINE__, data_c * sizeof (signed char));
const uint16_t V_MOVEMENT_FILTER_MSK = 0xFFFF; // Bitmask to get last 16 Bits
+
rv = EXIT_FAILURE;
const uint16_t V_MOVEMENT_LANDED_MSK = 0x0000; // Bitmask to check for enough detected sequential non-movements
+
goto MEASURE_ERR_FREE_TYPES;
const uint16_t  V_MOVEMENT_FLIGHING_MSK = V_MOVEMENT_FILTER_MSK; // Bitmask to check for enough detected sequential movements
+
}
 +
m->data = calloc(data_c, sizeof (double));
 +
if (NULL == m->data && data_c > 0) {
 +
fprintf(stderr, "%s:%i: calloc of %zu bytes failed\n", __FILE__, __LINE__, data_c * sizeof (double));
 +
rv = EXIT_FAILURE;
 +
goto MEASURE_ERR_FREE_PRIORITIES;
 +
}
 +
 
 +
for (size_t i = 0; data_c > i; i++) {
 +
m->types[i] = types[i];
 +
m->priorities[i] = priorities[i];
 +
}
  
double height_last = 0.0; // Last height value
 
double height_cur = 0.0; // Current height value
 
double height_cnt = 0.0; // Counter for all height values, also false values
 
double dheight_sum = 0.0; // Sum of delta height values
 
  
double v_velo = 0.0; // vertical velocity in m/s
+
if (EXIT_FAILURE == rv) {
uint16_t v_movement_seq = 0; // Bit counter for sequential vertical movement
+
MEASURE_ERR_FREE_PRIORITIES:
 +
free(m->priorities);
 +
MEASURE_ERR_FREE_TYPES:
 +
free(m->types);
  
const long MASTER_LED_MS = 500;
+
}
const long SENSORS_LED_MS = 100;
+
m->data_c = data_c;
int master_LED_on = 0;
 
int sensors_LED_on = 0;
 
  
 +
return rv;
 +
}
  
// Camera configurations
+
int measurement_destroy(measurement *const m) {
const camera_mode USB_CAMERA_MODE = PHOTO;
+
if (NULL == m) {
const char *USB_CAMERA_FILENAME = "imgs/camera_usb_bottom";
+
fprintf(stderr, "%s:%i: Input m is Null\n", __FILE__, __LINE__);
const camera_parameters USB_CAMERA_PARM = {
+
return EXIT_FAILURE;
0,
+
}
10,   // freq, 1 picture per x seconds
 
// 1920, // width, too much CPU usage
 
// 1080, // height
 
// 1280, // width
 
// 720, // height
 
640, // width
 
480, // height
 
0
 
};
 
char usb_camera_is_active = 0;
 
  
const camera_mode RASPICAM_MODE = VIDEO;
+
// Has no effect, if pointer is NULL.
const char *RASPICAM_FILENAME = "vids/raspicam_side";
+
free(m->types);
const camera_parameters RASPICAM_PARM = {
+
free(m->data);
3 * 60 * 60,   // duration, 3 hours, should be larger than flight time
 
0,
 
1920, // width
 
1080, // height
 
30    // framerate
 
};
 
char raspicam_is_active = 0;
 
  
const long GSM_SEND_MS = 180000;  // Every 3 minutes.
+
// Reset attributes to avoid undefined behavior on later use.
 +
m->data_c = 0;
 +
m->types = NULL;
 +
m->data = NULL;
  
int tmp = 0; // For unrelated use.
+
return EXIT_SUCCESS;
 +
}
  
// FLOW #1
+
int measurement_append_to_csv_file(int fd, measurement m) {
// Check for root privileges (bcm2835 doesn't do that automatically).
+
size_t line_length = 0;
if ((uid = getuid()) && geteuid() == uid)
+
char *line = NULL;
{
+
ssize_t written = 0;
errno = EPERM;
+
 
fprintf(stderr, "%s:%i: root check failed: %s\n", __FILE__, __LINE__, strerror(errno));
+
int rv = EXIT_SUCCESS;
 +
 
 +
if (-1 == fcntl(fd, F_GETFD)) {
 +
fprintf(stderr, "%s:%i: invalid file handle given\n", __FILE__, __LINE__);
 
rv = EXIT_FAILURE;
 
rv = EXIT_FAILURE;
 
goto ERR;
 
goto ERR;
 
}
 
}
  
// FLOW #2.1
+
if (0 == m.data_c || NULL == m.data) {
// Setup signal handling
+
fprintf(stderr, "%s:%i: uninitialized measurement given\n", __FILE__, __LINE__);
// Maintenance handler
 
maintenance_action.sa_flags = 0;
 
sigemptyset(&maintenance_action.sa_mask);
 
sigaddset(&maintenance_action.sa_mask, MAINTENANCE_SIGNAL);
 
maintenance_action.sa_handler = sh_maintenance;
 
tmp = sigaction(MAINTENANCE_SIGNAL, &maintenance_action, &maintenance_action_default);
 
if (0 > tmp) {
 
fprintf(stderr, "%s:%d: setting maintenance handler failed\n", __FILE__, __LINE__);
 
 
rv = EXIT_FAILURE;
 
rv = EXIT_FAILURE;
 
goto ERR;
 
goto ERR;
 
}
 
}
// FLOW #2.2
+
 
// Phase update handler
+
 
update_action.sa_flags = 0;
+
 
sigemptyset(&update_action.sa_mask);
+
// Generate CSV line.
sigaddset(&update_action.sa_mask, UPDATE_SIGNAL);
+
line = measurement_to_csv(m);
update_action.sa_handler = sh_update_phase;
+
if (NULL == line) {
tmp = sigaction(UPDATE_SIGNAL, &update_action, &update_action_default);
+
fprintf(stderr, "%s:%i: generating CSV line failed\n", __FILE__, __LINE__);
if (0 > tmp) {
 
fprintf(stderr, "%s:%d: setting phase update handler failed\n", __FILE__, __LINE__);
 
 
rv = EXIT_FAILURE;
 
rv = EXIT_FAILURE;
goto ERR_SIG_MAINT;
+
goto ERR;
 
}
 
}
// Mask to block all signals
+
line_length = strlen(line);
tmp = sigfillset(&all);
+
 
if (-1 == tmp) {
+
// Write line to file.
fprintf(stderr, "%s:%i: filling blocking signal mask failed\n", __FILE__, __LINE__);
+
written = write(fd, line, line_length);
 +
if (0 > written) {
 +
fprintf(stderr, "%s:%d: writing CSV line failed: %s\n", __FILE__, __LINE__, strerror(errno));
 
rv = EXIT_FAILURE;
 
rv = EXIT_FAILURE;
goto ERR_SIG_UPDT;
+
goto ERR_FREE;
 
}
 
}
 
+
if (line_length != (size_t) written) {
// FLOW #3
+
fprintf(stderr, "%s:%d: writing CSV line failed: %zi/%zu bytes\n", __FILE__, __LINE__, written, line_length);
// Try reading a previously saved phase or instead use and write START.
 
tmp = read_phase(&active_phase);
 
if (EXIT_FAILURE == tmp) {
 
active_phase = START;
 
tmp = write_phase(active_phase);
 
if (EXIT_FAILURE == tmp) {
 
fprintf(stderr, "%s:%i: writing initial phase failed\n", __FILE__, __LINE__);
 
rv = EXIT_FAILURE;
 
goto ERR_SIG_UPDT;
 
}
 
}
 
// FLOW #3.1
 
// Update v_movement_seq
 
if (FLIGHT == active_phase) {
 
v_movement_seq = 0xFF;
 
}
 
 
 
// FLOW #4.1
 
// bcm2835_init() comes with error message printing to stderr.
 
if (!bcm2835_init()) {
 
fprintf(stderr, "%s:%i: bcm2835_init failed\n", __FILE__, __LINE__);
 
 
rv = EXIT_FAILURE;
 
rv = EXIT_FAILURE;
goto ERR_SIG_UPDT;
+
goto ERR_FREE;
 
}
 
}
  
// FLOW #4.2
+
ERR_FREE:
// Initialize I2C functionality.
+
free(line);
if (!bcm2835_i2c_begin()) {
 
fprintf(stderr, "%s:%i: I2C initialization failed\n", __FILE__, __LINE__);
 
rv = EXIT_FAILURE;
 
goto ERR_BCM_LIB_CLOSE;
 
}
 
  
// FLOW #5
+
ERR:
// Create and initialize array for connected sensors
+
return rv;
bcm2835_i2c_set_baudrate(MIN_BAUDRATE);
+
}
if (EXIT_FAILURE == sensor_create_init_connected_sensors(SENSORS, SENSOR_AMT, &s_con, &s_con_amt)) {
 
fprintf(stderr, "%s:%i: sensor_create_connected_sensors failed\n", __FILE__, __LINE__);
 
rv = EXIT_FAILURE;
 
goto ERR_BCM_I2C_END;
 
}
 
  
// FLOW #6
 
// Determine greatest common I2C-frequency
 
for (size_t i = 0; s_con_amt > i; i++) {
 
if (NULL != s_con[i].reg) {
 
if (baudrate > s_con[i].reg->i2c_freq_max) {
 
baudrate = MIN_BAUDRATE;
 
}
 
}
 
}
 
bcm2835_i2c_set_baudrate(baudrate);
 
  
// FLOW #7.1
+
void measurement_print(measurement m) {
sensor_get_best_by_date_type(s_con, s_con_amt, HEIGHT, &sensor_height);
+
printf("measurements = { ");
if (NULL == sensor_height) {
+
printf("time.tv_sec = %ld; ", m.time.tv_sec);
fprintf(stderr, "%s:%d: no height sensor found. terminate program.\n", __FILE__, __LINE__);
+
printf("time.tv_nsec = %ld; ", m.time.tv_nsec);
rv = EXIT_FAILURE;
+
printf("data_c = %zu; ", m.data_c);
goto ERR_S_CON_DESTROY;
+
printf("data = { ");
 +
for (size_t i = 0; m.data_c > i; i++) {
 +
printf("types[%zu] = %d; ", i, m.types[i]);
 +
printf("priorities[%zu] = %d; ", i, m.priorities[i]);
 +
printf("data[%zu] = %lf; ", i, m.data[i]);
 
}
 
}
 +
printf("}; ");
 +
printf("};");
 +
}
 +
  
// FLOW #7.2
+
</syntaxhighlight>
sensor_get_best_by_date_type(s_con, s_con_amt, POS_LAT, &sensor_gps);
+
</div>
if (NULL == sensor_gps) {
+
</div>
fprintf(stderr, "%s:%d: no GPS sensor found. Terminate program.\n", __FILE__, __LINE__);
 
rv = EXIT_FAILURE;
 
goto ERR_S_CON_DESTROY;
 
}
 
  
// FLOW #8.1
+
<div class="mw-collapsible mw-collapsed" style="width:100%">
// Find length of longest sensor name to allocate sufficient memory for file names.
+
;sensor.h
if (INT_MAX < FORMAT_FILE_ID_LENGTH) {
+
<div class="mw-collapsible-content">
fprintf(stderr, "%s:%i: FORMAT_FILE_ID_LENGTH too large: expected 0..%i, got %zu\n", __FILE__, __LINE__, INT_MAX, FORMAT_FILE_ID_LENGTH);
+
<syntaxhighlight lang="c">
rv = EXIT_FAILURE;
 
goto ERR_S_CON_DESTROY;
 
}
 
  
for (size_t i = 0; s_con_amt > i; ++i) {
+
/**
sensor_name_length = strlen(s_con[i].reg->name);
+
* @file  sensor.h
if (sensor_name_length > file_name_length) {
+
* @brief Definitions for all sensor programs.
file_name_length = sensor_name_length;
+
*
}
+
* @author Jonas Gaida, Florian Schwarz
}
+
  */
file_name_length += FORMAT_FILE_ID_LENGTH + 6; // Length of "_###.csv\0".
+
 
file_name = calloc(file_name_length, sizeof (char));
+
#ifndef INCLUDE_SENSOR_H_
if (NULL == file_name) {
+
#define INCLUDE_SENSOR_H_
fprintf(stderr, "%s:%i: calloc of %zu bytes failed\n", __FILE__, __LINE__, file_name_length * sizeof (char));
+
 
rv = EXIT_FAILURE;
+
#include <time.h>  // struct timespec
goto ERR_S_CON_DESTROY;
+
 
}
+
#include "measurement.h"  // measurement_data
 +
#include "task.h"  // task
 +
#include "definitions.h" // phase
  
// FLOW #8.2
 
// CSV-Preperations
 
for (size_t i = 0; s_con_amt > i; ++i) {
 
// Prepare filenames.
 
tmp = snprintf(file_name, file_name_length, "%s_%0*zu.csv", s_con[i].reg->name, (int) FORMAT_FILE_ID_LENGTH, i);
 
if (0 > tmp) {
 
fprintf(stderr, "%s:%i: buffering file name for %s failed\n", __FILE__, __LINE__, s_con[i].reg->name);
 
rv = EXIT_FAILURE;
 
goto ERR_FILE_NAME_FREE;
 
}
 
  
// Open files.
+
/**
tmp = access(file_name, F_OK); // Check if file exists (for CSV header).
+
* @brief Possible I2C bus frequencies.
s_con[i].csv_file = open(file_name, O_CREAT | O_WRONLY | O_APPEND, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
+
*
if (0 > s_con[i].csv_file) {
+
* Currently only values which are both within the official I2C specification
fprintf(stderr, "%s:%i: opening file %s failed: %s\n", __FILE__, __LINE__, file_name, strerror(errno));
+
* and also part of the valid clock dividers in the bcm2835 library are
rv = EXIT_FAILURE;
+
* available.
goto ERR_FILE_NAME_FREE;
+
*/
}
+
typedef enum baudrate {
// If file did not exist, write header.
+
BAUD_100KHZ = 100000,    ///< The original mode at 100 kHz.
if (tmp) {
+
BAUD_400KHZ = 400000    ///< "Fast-mode" at 400 kHz.
errno = 0;
+
 
written = write(s_con[i].csv_file, s_con[i].reg->csv_header, strlen(s_con[i].reg->csv_header));
+
// Additional frequencies from the I2C specification.
if (0 > written) {
+
// BAUD_1000KHZ = 1000000,  ///< "Fast-mode plus" at 1 MHz.
fprintf(stderr, "%s:%d: writing CSV header failed: %s\n", __FILE__, __LINE__, strerror(errno));
+
// BAUD_3400KHZ = 3400000, ///< "High-speed mode" at 3.4 MHz.
rv = EXIT_FAILURE;
+
// BAUD_5000KHZ = 5000000, ///< "Ultra Fast-mode" at 5 MHz (unidirectional).
goto ERR_FILE_NAME_FREE;
+
 
}
+
// Additional frequencies from the bcm2835 library based on a 250 MHz clock.
}
+
// BAUD_1666KHZ = 1666667,  ///< BCM2835_I2C_CLOCK_DIVIDER_150
}
+
// BAUD_1689KHZ = 1689189  ///< BCM2835_I2C_CLOCK_DIVIDER_148
 +
} baudrate;
 +
 
 +
#define MIN_BAUDRATE BAUD_100KHZ
 +
#define MAX_BAUDRATE BAUD_400KHZ
 +
 
 +
 
 +
/**
 +
* @brief Possible sensor states controlled by master
 +
*/
 +
typedef enum s_state {
 +
UNKNOWN = 0x00, ///< Default on-start state
 +
UNREACHABLE,     ///< If first connection failed, never touch s again
 +
INIT,            ///< If first connection success, init remaining or reconnected
 +
MEASURE,         ///< If init finished, measure remaining or measure successful
 +
SLEEP,          ///< If no active phase
 +
FAILURE          ///< If consecutive failed communications > x, try is_connected all y seconds, if success, init
 +
} s_state;
  
// FLOW #9.1
 
// Create tasks
 
tasks_amt = s_con_amt + TASK_TYPE_AMT - 1;
 
tasks = malloc(tasks_amt * sizeof (struct task));
 
if (NULL == tasks) {
 
fprintf(stderr, "%s:%i: malloc of %zu bytes failed\n", __FILE__, __LINE__, file_name_length * sizeof (char));
 
rv = EXIT_FAILURE;
 
goto ERR_FILE_NAME_FREE;
 
}
 
// FLOW #9.2
 
// Init tasks
 
// Sensors
 
for (size_t i = 0; s_con_amt > i; i++) {
 
tasks[i].type = SENSOR;
 
tasks[i].state = RUNNING;
 
tasks[i].next_time = (struct timespec) {0, 0};
 
tasks[i].p = &(s_con[i]);
 
sensor = (i2c_sensor *) (tasks[i].p);
 
tasks[i].active_phases = sensor->reg->active_phases;
 
s_con[i].t = &(tasks[i]);
 
  
}
+
/**
// LED tasks
+
* @brief Sensor interface.
tasks[s_con_amt].type = LED_MASTER;
+
*/
tasks[s_con_amt].state = RUNNING;
+
typedef struct i2c_sensor_reg {
tasks[s_con_amt].active_phases = START | ASCEND;
+
/**
errno = 0;
+
* @brief Sensor name.
if (0 > clock_gettime(CLOCK_MONOTONIC, &(tasks[s_con_amt].next_time))) {
+
*
fprintf(stderr, "%s:%i: acquiring time failed: %s\n", __FILE__, __LINE__, strerror(errno));
+
* The name is used for the creation of the data files, error messages and
rv = EXIT_FAILURE;
+
* for easier identification by the user.
goto ERR_TASKS_FREE;
+
*/
}
+
const char *const name;
tasks[s_con_amt + 1].type = LED_SENSORS;
+
 
tasks[s_con_amt + 1].state = RUNNING;
+
/**
tasks[s_con_amt + 1].active_phases = START | ASCEND;
+
* @brief Bit mask containing the phases during which the sensor is active.
errno = 0;
+
*/
if (0 > clock_gettime(CLOCK_MONOTONIC, &(tasks[s_con_amt + 1].next_time))) {
+
const phase active_phases;
fprintf(stderr, "%s:%i: acquiring time failed: %s\n", __FILE__, __LINE__, strerror(errno));
+
 
rv = EXIT_FAILURE;
+
/**
goto ERR_TASKS_FREE;
+
* @brief Length of a full measurement cycle of this sensor in milliseconds.
}
+
*
// Fallback task for state change, if sensors or state transitions fail
+
* Should not be shorter than sum of measure delays!
tasks[s_con_amt + 2].type = FALLBACK_STATE_TO_RECOVERY_CHANGER;
+
*/
tasks[s_con_amt + 2].state = RUNNING;
+
const unsigned long cycle_len;
tasks[s_con_amt + 2].active_phases = START | FLIGHT | RECOVERY;
 
tasks[s_con_amt + 2].next_time = (struct timespec) {0, 0};
 
tasks[s_con_amt + 2].p = NULL;
 
// Camera USB task
 
tasks[s_con_amt + 3].type = CAM_USB_TASK;
 
tasks[s_con_amt + 3].state = RUNNING;
 
tasks[s_con_amt + 3].active_phases = START | FLIGHT | RECOVERY;
 
tasks[s_con_amt + 3].next_time = (struct timespec) {0, 0};
 
// Camera raspi task
 
tasks[s_con_amt + 4].type = CAM_RASPI_TASK;
 
tasks[s_con_amt + 4].state = RUNNING;
 
tasks[s_con_amt + 4].active_phases = START | FLIGHT | RECOVERY;
 
tasks[s_con_amt + 4].next_time = (struct timespec) {0, 0};
 
// GSM sender task
 
tasks[s_con_amt + 5].type = GSM_TASK;
 
tasks[s_con_amt + 5].state = RUNNING;
 
tasks[s_con_amt + 5].active_phases = START | FLIGHT | RECOVERY;
 
errno = 0;
 
if (0 > clock_gettime(CLOCK_MONOTONIC, &(tasks[s_con_amt + 5].next_time))) {
 
fprintf(stderr, "%s:%i: acquiring time failed: %s\n", __FILE__, __LINE__, strerror(errno));
 
rv = EXIT_FAILURE;
 
goto ERR_TASKS_FREE;
 
}
 
// Watchdog task
 
tasks[s_con_amt + 6].type = WATCHDOG;
 
tasks[s_con_amt + 6].state = RUNNING;
 
tasks[s_con_amt + 6].active_phases = START | FLIGHT | RECOVERY;
 
errno = 0;
 
if (0 > clock_gettime(CLOCK_MONOTONIC, &(tasks[s_con_amt + 6].next_time))) {
 
fprintf(stderr, "%s:%i: acquiring time failed: %s\n", __FILE__, __LINE__, strerror(errno));
 
rv = EXIT_FAILURE;
 
goto ERR_TASKS_FREE;
 
}
 
// Watchpuppy task
 
tasks[s_con_amt + 7].type = WATCHPUPPY;
 
tasks[s_con_amt + 7].state = RUNNING;
 
tasks[s_con_amt + 7].active_phases = START | FLIGHT | RECOVERY;
 
errno = 0;
 
if (0 > clock_gettime(CLOCK_MONOTONIC, &(tasks[s_con_amt + 7].next_time))) {
 
fprintf(stderr, "%s:%i: acquiring time failed: %s\n", __FILE__, __LINE__, strerror(errno));
 
rv = EXIT_FAILURE;
 
goto ERR_TASKS_FREE;
 
}
 
  
// FLOW #10
+
/**
// Update states by phases
+
* @brief Pointer to array of measurement types
if (EXIT_FAILURE == task_update_states_by_phase(tasks, tasks_amt, active_phase)) {
+
*
fprintf(stderr, "%s:%i: task_update_states_by_phase failed\n", __FILE__, __LINE__);
+
* Needs to be in sync with measurement data
rv = EXIT_FAILURE;
+
*/
goto ERR_TASKS_FREE;
+
const date_type *const types;
}
 
  
// Main loop
+
/**
while (QUIT != active_phase && EXIT_SUCCESS == task_get_next(tasks, tasks_amt, &task)) {
+
* @brief Pointer to array of measurement priorities
 +
*
 +
* Needs to be in sync with measurement data
 +
*/
 +
const signed char *const priorities;
  
// Is task ready? If not, sleep remaining time
+
/**
if (0 > clock_gettime(CLOCK_MONOTONIC, &cur_time)) {
+
* @brief Amount of data packages to deliver
fprintf(stderr, "%s:%i: acquiring time failed: %s\n", __FILE__, __LINE__, strerror(errno));
+
*
rv = EXIT_FAILURE;
+
* Needs to be in sync with measurement data
goto ERR_TASKS_FREE;
+
*/
}
+
const size_t date_amt;
if (timespec_gt(task->next_time, cur_time)) {
 
diff_time = timespec_sub(task->next_time, cur_time);
 
do {
 
errno = 0;
 
} while (0 > clock_nanosleep(CLOCK_MONOTONIC, 0,&diff_time, &diff_time) && EINTR == errno);
 
}
 
  
switch (task->type) {
+
/**
case SENSOR:
+
* @brief Header line for CSV file (containing newline symbol) as
 +
*        null-terminated string.
 +
*/
 +
const char *const csv_header;
  
sensor = (i2c_sensor*) (task->p);
+
/**
// Sensor measurement cycle
+
* @brief The maximum frequency of the I2C bus supported by this sensor.
// Repeat, as long as sensor isn't sleeping and there's no delay
+
*/
do {
+
const baudrate i2c_freq_max;
  
// TODO-DEL - Signal-Blocking while sensors running not useful in case of program freezing
+
/**
// // Block all signals.
+
* @brief Initialization of the sensor.
// tmp = sigprocmask(SIG_BLOCK, &all, &normal);
+
*
// if (-1 == tmp) {
+
* For sensors that need calibration or other initialization.
// fprintf(stderr, "%s:%i: setting blocking signal mask failed\n", __FILE__, __LINE__);
+
* This function is also called after leaving FAILURE or SLEEP state.
// }
+
* So beware, if some initilizations are dependent of START state.
 +
* (e.g. h_offset with launch location height and pressure.)
 +
*/
 +
int (*init)(phase p, signed char *const it_rem, long *const processing_time);
 +
/**
 +
* @brief Collecting measure data from sensor.
 +
*
 +
* Collection is finished if iterations_rem is <= 0.
 +
* After state transition master sets it_rem < 0, so sensor can recognize
 +
* first entry.
 +
*
 +
*/
 +
int (*measure)(phase p, measurement *const m, signed char *const it_rem, long *const processing_time);
  
switch (sensor->state) {
+
/**
 +
* @brief I2C-Connectivity-Test to sensor, prior to an initializiation
 +
*
 +
* Is used in the beginning to determin connected sensors and to test in-flight
 +
* failed sensors for reinitialization
 +
*/
 +
int (*is_connected)(void);
  
case INIT:
+
} i2c_sensor_reg;
if (EXIT_FAILURE == sensor->reg->init(active_phase, &(sensor->it_rem), &(sensor->processing_time))) { // Init_Err
+
 
fprintf(stderr, "%s:%i: init for %s failed\n", __FILE__, __LINE__, sensor->reg->name);
+
 
sensor->err_seq++;
+
 
sensor->err_total++;
+
/*
if (ERR_THRESHOLD <= sensor->err_seq) {
+
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
sensor->state = FAILURE;
+
* !!                                                                        !!
sensor->failure_total++;
+
* !!      REST OF FILE IS NOT PART OF INTERFACE BUT IS USED BY MASTER      !!
if (sensor == sensor_height) {
+
* !!                                                                        !!
sensor_get_best_by_date_type(s_con, s_con_amt, HEIGHT, &sensor_height);
+
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
}
+
*/
sensor->processing_time = ERR_PERIOD_MS;
+
 
} else {
+
 
sensor->processing_time = ERR_SEQ_DELAY_MS;
+
 
}
+
/**
} else { // Init_Succ
+
* @brief Sensor struct
sensor->err_seq = 0;
+
*/
if (0 < sensor->processing_time) {
+
typedef struct i2c_sensor {
// Update next_time
+
/**
if (0 > clock_gettime(CLOCK_MONOTONIC, &cur_time)) {
+
* @brief Pointer to corresponding task // TODO-REVAL - Necessary?
fprintf(stderr, "%s:%i: acquiring time failed: %s\n", __FILE__, __LINE__, strerror(errno));
+
*/
rv = EXIT_FAILURE;
+
task *t;
goto ERR_TASKS_FREE;
+
/**
}
+
* @brief Pointer to original i2c_sensor_reg.
task->next_time = timespec_add_ms(cur_time, sensor->processing_time);
+
*/
}
+
const i2c_sensor_reg *reg;
if (0 < sensor->it_rem) { // Init_Unfinished
+
/**
sensor->it_rem--;
+
* @brief Sensor state.
} else { // Init_Finished
+
*/
sensor->state = MEASURE;
+
s_state state;
sensor->it_rem = -1;
+
/**
// Set sensor->next_cycle_time
+
* @brief Counter for multiple function calls (e.g. init or measure)
if (0 > clock_gettime(CLOCK_MONOTONIC, &(sensor->next_cycle_time))) {
+
*
fprintf(stderr, "%s:%i: acquiring time failed: %s\n", __FILE__, __LINE__, strerror(errno));
+
* Sensor function will get called additionally as often as this property is set.
rv = EXIT_FAILURE;
+
* After state transition, this gets set to < 0, so sensor can recognize first
goto ERR_TASKS_FREE;
+
* entry. Then in first call sensor may set an aproporiate value
}
+
* (e.g. 2 for two further iterations, so with current in total 3 rounds).
sensor->next_cycle_time = timespec_add_ms(sensor->next_cycle_time, sensor->reg->cycle_len * CYCLE_LEN_MULT);
+
* After leaving master checks if this is <= 0 and if true switches to next state.
}
+
* Decrement is handled by master.
}
+
*/
break;
+
signed char it_rem;
case MEASURE:
+
/**
if (EXIT_FAILURE == sensor->reg->measure(active_phase, &(sensor->measurements), &(sensor->it_rem), &(sensor->processing_time))) { // Measure_Err
+
* @brief Time which the sensor needs to finish current action
fprintf(stderr, "%s:%i: measure for %s failed\n", __FILE__, __LINE__, sensor->reg->name);
+
*/
sensor->err_seq++;
+
long processing_time;
sensor->err_total++;
+
/**
if (ERR_THRESHOLD <= sensor->err_seq) {
+
* @brief Time for a complete sensor cycle to redeem s_cycle_len.
sensor->state = FAILURE;
+
*
sensor->failure_total++;
+
* After finishing a full cycle s_cycle_len is added
if (sensor == sensor_height) {
+
*/
sensor_get_best_by_date_type(s_con, s_con_amt, HEIGHT, &sensor_height);
+
struct timespec next_cycle_time;
}
+
/**
sensor->processing_time = 0;
+
* @brief Struct to fill with one measurement round
} else {
+
*/
sensor->processing_time = ERR_SEQ_DELAY_MS;
+
measurement measurements;
}
+
/**
} else { // Measure_Succ
+
* @brief Destination file handler for csv output
sensor->err_seq = 0;
+
*/
if (0 < sensor->it_rem) { // Measure_Unfinished
+
int csv_file;
sensor->it_rem--;
+
/**
if (0 < sensor->processing_time) {
+
* @brief Sequential error counter
// Update next_time
+
*/
if (0 > clock_gettime(CLOCK_MONOTONIC, &cur_time)) {
+
unsigned char err_seq;
fprintf(stderr, "%s:%i: acquiring time failed: %s\n", __FILE__, __LINE__, strerror(errno));
+
unsigned long err_total;
rv = EXIT_FAILURE;
+
unsigned long failure_total;
goto ERR_TASKS_FREE;
+
 
}
+
} i2c_sensor;
task->next_time = timespec_add_ms(cur_time, sensor->processing_time);
 
}
 
} else { // Measure_Finished
 
sensor->it_rem = -1;
 
// TODO-UC
 
// task->next_time = timespec_add_ms(sensor->next_cycle_time, sensor->processing_time);
 
// sensor->next_cycle_time = timespec_add_ms(sensor->next_cycle_time, sensor->reg->cycle_len * CYCLE_LEN_MULT);
 
  
// TODO-TEST-2-B
 
if (0 > clock_gettime(CLOCK_MONOTONIC, &cur_time)) {
 
fprintf(stderr, "%s:%i: acquiring time failed: %s\n", __FILE__, __LINE__, strerror(errno));
 
rv = EXIT_FAILURE;
 
goto ERR_TASKS_FREE;
 
}
 
task->next_time = timespec_add_ms(cur_time, sensor->processing_time);
 
sensor->next_cycle_time = timespec_add_ms(sensor->next_cycle_time, sensor->reg->cycle_len * CYCLE_LEN_MULT);
 
if (timespec_gt(sensor->next_cycle_time, task->next_time)) {
 
task->next_time = sensor->next_cycle_time;
 
} else {
 
fprintf(stderr, "%s:%i: %ld.%03ld: sensor %s cycle_len exceeded\n", __FILE__, __LINE__, cur_time.tv_sec, cur_time.tv_nsec / 1000000, sensor->reg->name);
 
sensor->next_cycle_time = task->next_time;
 
}
 
// TODO-TEST-2-E
 
  
sensor->processing_time = 1; // Actual value doesn't matter, just to stop sensor loop
+
/**
if (EXIT_FAILURE == measurement_append_to_csv_file(sensor->csv_file, sensor->measurements)) {
+
* @brief Creates i2c_sensor
fprintf(stderr, "%s:%i: append_to_csv_file for %s's data failed\n", __FILE__, __LINE__, sensor->reg->name);
+
*
}
+
* Allocates necessary memory for i2c_sensor struct and sub structures
// Test for phase switching conditions.
+
* specified by data from s_reg.
if (sensor_height == sensor) { // New height value?
+
*
// TODO-MV - move to sensor_height probing an save in pointer
+
* @param[in] s_reg i2c_sensor_reg, wherefrom to take specification data
for (size_t i = 0; sensor->reg->date_amt > i; i++) { // get height
+
* @param[out] s Pointer to Pointer of i2c_sensor struct
if (HEIGHT == sensor->reg->types[i]) {
+
*
height_cur = sensor->measurements.data[i];
+
* @return @c EXIT_SUCCESS if successful else @c EXIT_FAILURE.
break;
+
*/
}
+
int sensor_create(const i2c_sensor_reg s_reg, i2c_sensor **s);
}
+
 
if (HEIGHT_MIN < height_cur || HEIGHT_MAX > height_cur) { // No false value?
 
dheight_sum += height_cur - height_last;
 
height_last = height_cur;
 
}
 
height_cnt++;
 
if (HEIGHT_AMT == height_cnt) { // Enough values collected? Calculate vert. velocity
 
v_velo = dheight_sum / height_cnt / (double) sensor->reg->cycle_len * 1000;
 
height_cnt = 0.0;
 
dheight_sum = 0.0;
 
  
if (v_velo > V_VELO_HALT_TO_FLIGHT_THRESHOLD || v_velo < (-1 * V_VELO_HALT_TO_FLIGHT_THRESHOLD)) { // Threshold to detect movement exceeded?
+
/**
v_movement_seq = v_movement_seq << 1 | 0x01;
+
* @brief Creates only i2c_sensor substructures
if (START == active_phase && V_MOVEMENT_FLIGHING_MSK == (v_movement_seq & V_MOVEMENT_FILTER_MSK)) { // In START pahse and enough movements detected?
+
*
printf("Phase switched to FLIGHT!\n");
+
* If an array of i2c_sensors already exists, this function may come handy.
active_phase = FLIGHT;
+
* Allocates necessary memory for i2c_sensor sub structures specified
tmp = write_phase(active_phase);
+
* by data from s_reg.
if (EXIT_FAILURE == tmp) {
+
*
fprintf(stderr, "%s:%i: writing updated phase failed: active_phase=%u\n", __FILE__, __LINE__, active_phase);
+
* @param[in] s_reg i2c_sensor_reg with specifying data for allocation
}
+
* @param[out] s Pointer to i2c_sensor struct
// Update states by phases
+
*
if (EXIT_FAILURE == task_update_states_by_phase(tasks, tasks_amt, active_phase)) {
+
* @return @c EXIT_SUCCESS if successful else @c EXIT_FAILURE.
fprintf(stderr, "%s:%i: task_update_states_by_phase failed\n", __FILE__, __LINE__);
+
*/
rv = EXIT_FAILURE;
+
int sensor_create_sub(const i2c_sensor_reg s_reg, i2c_sensor *s);
goto ERR_TASKS_FREE;
+
 
}
+
 
}
+
/**
} else { // Thresehold not exceeded
+
* @brief Initialize i2c_sensor
v_movement_seq = (v_movement_seq << 1) | 0x0000;
+
*
if (FLIGHT == active_phase && V_MOVEMENT_LANDED_MSK == (v_movement_seq & V_MOVEMENT_FILTER_MSK)) { // Enough stationaries detected?
+
* Initializes i2c_sensor by data from s_reg.
printf("Phase switched to RECOVERY!\n");
+
*
active_phase = RECOVERY;
+
* @param[in] s_reg i2c_sensor_reg, wherefrom to take init data
tmp = write_phase(active_phase);
+
* @param[out] s Pointer to i2c_sensor struct
if (EXIT_FAILURE == tmp) {
+
*
fprintf(stderr, "%s:%i: writing updated phase failed: active_phase=%u\n", __FILE__, __LINE__, active_phase);
+
* @return @c EXIT_SUCCESS if successful else @c EXIT_FAILURE.
}
+
*/
// Update states by phases
+
int sensor_init(const i2c_sensor_reg *const s_reg, i2c_sensor *s);
if (EXIT_FAILURE == task_update_states_by_phase(tasks, tasks_amt, active_phase)) {
+
 
fprintf(stderr, "%s:%i: task_update_states_by_phase failed\n", __FILE__, __LINE__);
+
 
rv = EXIT_FAILURE;
+
/**
goto ERR_TASKS_FREE;
+
* @brief Initialize i2c_sensor, except task pointer and error values
}
+
*
}
+
* Initializes i2c_sensor by data from s_reg.
}
+
*
// printf("s_name = %s\nheight_cur = %lf\nv_velo = %lf\nv_movement_seq = 0x%04x\n\n", sensor->reg->name, height_cur, v_velo, v_movement_seq); // TODO-DEL
+
* @param[in] s_reg i2c_sensor_reg, wherefrom to take init data
}
+
* @param[out] s Pointer to i2c_sensor struct
}
+
*
}
+
* @return @c EXIT_SUCCESS if successful else @c EXIT_FAILURE.
}
+
*/
break;
+
int sensor_reset(const i2c_sensor_reg *const s_reg, i2c_sensor *s);
case FAILURE:
+
 
if (EXIT_FAILURE == sensor->reg->is_connected()) { // Sensor still dead
+
 
fprintf(stderr, "%s:%i: reconnect for %s failed\n", __FILE__, __LINE__, sensor->reg->name);
+
/**
sensor->err_total++;
+
* @brief Probes in an i2c_sensor_reg array for connected sensors, allocates necessary recources and initializes them.
sensor->processing_time = ERR_PERIOD_MS;
+
*
task->next_time = timespec_add_ms(task->next_time, sensor->processing_time);
+
* To probe is_connected() is called for each sensor.
} else { // Sensor recovered
+
* Appropriate memory gets allocated for s_con and it gets
fprintf(stderr, "%s:%i: reconnect for %s successful\n", __FILE__, __LINE__, sensor->reg->name);
+
* initialized with given i2c_sensor_reg data.
sensor->err_seq = 0;
+
*
sensor->state = INIT;
+
* @param[in] s_reg Pointer to i2c_sensor_reg array, which to probe
sensor->it_rem = -1;
+
* @param[in] amt_s_reg Size of i2c_sensor_reg array s
if (EXIT_SUCCESS == sensor_has_date_type(*sensor, HEIGHT)) {
+
* @param[out] s_con Pointer to uninitialized i2c_sensor pointer
sensor_get_best_by_date_type(s_con, s_con_amt, HEIGHT, &sensor_height);
+
* @param[out] amt_con Pointer to size of i2c_sensor
}
+
*
sensor->processing_time = 0;
+
* @return @c EXIT_SUCCESS if successful else @c EXIT_FAILURE.
}
+
*/
break;
+
int sensor_create_init_connected_sensors(const i2c_sensor_reg *const s_reg, size_t amt_s_reg, i2c_sensor **s_con, size_t *amt_con);
default:
 
fprintf(stderr, "%s:%i: sensor \"%s\" has illegal state: %d. Stopped sensor task\n", __FILE__, __LINE__, sensor->reg->name, sensor->state);
 
task->state = STOPPED;
 
}
 
// TODO-DEL - Signal-Blocking while sensors running not useful in case of program freezing
 
// // Reset signal mask.
 
// tmp = sigprocmask(SIG_SETMASK, &normal, NULL);
 
// if (-1 == tmp) {
 
// fprintf(stderr, "%s:%i: returning to normal signal mask failed\n", __FILE__, __LINE__);
 
// }
 
} while (SLEEP != sensor->state && 0 >= sensor->processing_time);
 
break;
 
  
case LED_MASTER:
 
if (START == active_phase) {
 
// Blinks if master is running.
 
master_LED_on = !master_LED_on;
 
set_taskLED(task->type, master_LED_on);
 
  
task->next_time = timespec_add_ms(task->next_time, MASTER_LED_MS);
+
/**
} else {
+
* @brief Prints info about sensor_reg
set_taskLED(task->type, 0);
+
*
task->state = STOPPED;
+
* @param[in] reg i2c_sensor to print
}
+
*/
break;
+
void sensor_reg_print(i2c_sensor_reg reg);
 +
 
  
case LED_SENSORS:
+
/**
if (START == active_phase) {
+
* @brief Prints info about sensor
// Count current failures.
+
*
tmp = 0;
+
* @param[in] s i2c_sensor to print
for (size_t i = 0; i < s_con_amt; ++i) {
+
*/
if (FAILURE == s_con[i].state) {
+
void sensor_print(i2c_sensor s);
++tmp;
 
}
 
}
 
  
// Disconnected sensors or current failures -> off.
 
if (SENSOR_AMT != s_con_amt || tmp) {
 
sensors_LED_on = 0;
 
// Any past failures -> blinking; no failures at all -> on.
 
} else {
 
tmp = 0;
 
for (size_t i = 0; i < s_con_amt; ++i) {
 
tmp += s_con[i].failure_total;
 
}
 
sensors_LED_on = tmp ? !sensors_LED_on : 1;
 
}
 
set_taskLED(task->type, sensors_LED_on);
 
  
task->next_time = timespec_add_ms(task->next_time, SENSORS_LED_MS);
+
/**
} else {
+
* @brief Tests if sensor @p s provides measurements of type @p type
set_taskLED(task->type, 0);
+
*
task->state = STOPPED;
+
* @param[in] s i2c_sensor, which to search
}
+
* @param[in] type The wanted date_type
break;
+
*
 +
* @return @c EXIT_SUCCESS if successful else @c EXIT_FAILURE.
 +
*/
 +
int sensor_has_date_type(i2c_sensor s, date_type type);
  
case CAM_RASPI_TASK:
 
switch (active_phase) {
 
case RECOVERY:
 
if (raspicam_is_active) {
 
CAMERAS[0].stop();
 
raspicam_is_active = 0;
 
}
 
break;
 
default: // START, FLIGHT, etc
 
if (!raspicam_is_active) {
 
CAMERAS[0].start(RASPICAM_MODE, RASPICAM_FILENAME, RASPICAM_PARM);
 
raspicam_is_active = 1;
 
}
 
}
 
task->state = STOPPED;
 
break;
 
  
case CAM_USB_TASK:
+
/**
switch (active_phase) {
+
* @brief Delivers working sensor with highest priority on in type specified date_type
case RECOVERY:
+
*
if (raspicam_is_active) {
+
* If equal priorities, then first match is returned.
CAMERAS[1].stop();
+
*
usb_camera_is_active = 0;
+
* @param[in] s Pointer to i2c_sensor array, which to search
}
+
* @param[in] amt Size of i2c_sensor array s
break;
+
* @param[in] type The wanted date_type
default: // START, FLIGHT, etc
+
* @param[out] sensor Pointer to working height sensor with highest priority
if (!usb_camera_is_active) {
+
*
CAMERAS[1].start(USB_CAMERA_MODE, USB_CAMERA_FILENAME, USB_CAMERA_PARM);
+
* @return @c EXIT_SUCCESS if successful else @c EXIT_FAILURE.
usb_camera_is_active = 1;
+
*/
}
+
int sensor_get_best_by_date_type(i2c_sensor *s, size_t amt, date_type type, i2c_sensor **sensor);
}
 
task->state = STOPPED;
 
break;
 
  
case GSM_TASK:
 
// send_GSM(&sensor_gps->measurements); // TODO-UC
 
task->next_time = timespec_add_ms(task->next_time, GSM_SEND_MS);
 
break;
 
  
case FALLBACK_STATE_TO_RECOVERY_CHANGER:
+
#endif  // INCLUDE_SENSOR_H_
if (EXIT_FAILURE == fallback_recovery_task(task, &active_phase, cur_time)) {
 
fprintf(stderr, "%s:%s:%i: fallback_recovery_task failed\n", __FILE__, __func__, __LINE__);
 
rv = EXIT_FAILURE;
 
goto ERR_TASKS_FREE;
 
}
 
break;
 
  
case WATCHDOG:
+
</syntaxhighlight>
wake_watchdog();
+
</div>
task->next_time = timespec_add_ms(task->next_time, WATCHDOG_KEEPALIVE_MS);
+
</div>
break;
 
  
case WATCHPUPPY:
+
<div class="mw-collapsible mw-collapsed" style="width:100%">
system(WATCHPUPPY_SIGNALING_CMD);
+
;sensor.c
task->next_time = timespec_add_ms(task->next_time, WATCHPUPPY_SIGNALING_CYCLE_S * 1000);
+
<div class="mw-collapsible-content">
break;
+
<syntaxhighlight lang="c">
 +
 
 +
/**
 +
* @file  sensor.c
 +
* @brief Implementation of sensor.h
 +
*
 +
* @author Florian Schwarz
 +
*/
 +
 
 +
#include "sensor.h"
  
default:
+
#include <stdlib.h>  // exit codes
fprintf(stderr, "%s:%i: task with faulty task_type\n", __FILE__, __LINE__);
+
#include <stdio.h> // fprintf
}
+
#include <errno.h>  // errno
}
+
#include <string.h>  // strerror
  
// Cleanup in reverse order.
+
int sensor_create(const i2c_sensor_reg s_reg, i2c_sensor **s) {
ERR_TASKS_FREE:
+
int rv = EXIT_SUCCESS;
free(tasks);
+
int rv_f;
ERR_FILE_NAME_FREE:
+
if (NULL == s) {
free(file_name);
+
fprintf(stderr, "%s:%i: Input s is NULL\n", __FILE__, __LINE__);
ERR_S_CON_DESTROY:
+
return EXIT_FAILURE;
for (size_t i = 0; s_con_amt > i; i++) {
 
measurement_destroy(&(s_con[i].measurements));
 
 
}
 
}
free(s_con);
+
// Allocate i2c and let the rest sensor_create_sub() do
ERR_BCM_I2C_END:
+
errno = 0;
bcm2835_i2c_end();
+
*s = malloc(sizeof (i2c_sensor));
ERR_BCM_LIB_CLOSE:
+
if (NULL == (*s) && ENOMEM == errno) {
if (!bcm2835_close()) {
+
fprintf(stderr, "%s:%i: malloc failed: %s\n", __FILE__, __LINE__, strerror(errno));
rv = EXIT_FAILURE;
+
return EXIT_FAILURE;
 
}
 
}
ERR_SIG_UPDT:
+
rv_f = sensor_create_sub(s_reg, *s);
tmp = sigaction(UPDATE_SIGNAL, &update_action_default, NULL);
+
if (EXIT_FAILURE == rv_f) {
if (0 > tmp) {
+
fprintf(stderr, "%s:%i: sensor_create_sub failed\n", __FILE__, __LINE__);
fprintf(stderr, "%s:%d: resetting phase update handler to default failed\n", __FILE__, __LINE__);
 
 
rv = EXIT_FAILURE;
 
rv = EXIT_FAILURE;
 +
 
}
 
}
ERR_SIG_MAINT:
+
 
tmp = sigaction(MAINTENANCE_SIGNAL, &maintenance_action_default, NULL);
+
if (EXIT_FAILURE == rv) {
if (0 > tmp) {
+
free(*s);
fprintf(stderr, "%s:%d: resetting maintenance handler to default failed\n", __FILE__, __LINE__);
 
rv = EXIT_FAILURE;
 
 
}
 
}
ERR:
 
 
return rv;
 
return rv;
 
}
 
}
  
 +
int sensor_create_sub(const i2c_sensor_reg s_reg, i2c_sensor *s) {
 +
if (NULL == s) {
 +
fprintf(stderr, "%s:%i: Input s is NULL\n", __FILE__, __LINE__);
 +
return EXIT_FAILURE;
 +
}
  
int read_phase(phase *const p) {
+
// Catch faulty registered sensors
FILE *file = NULL;
 
int tmp = 0;
 
int rv = EXIT_SUCCESS;
 
  
if (NULL == p) {
+
if (0 == s_reg.cycle_len) {
fprintf(stderr, "%s:%i: no phase given\n", __FILE__, __LINE__);
+
fprintf(stderr, "%s:%i: Sensor %s cycle_len is 0\n", __FILE__, __LINE__, s_reg.name);
 
return EXIT_FAILURE;
 
return EXIT_FAILURE;
 
}
 
}
 
+
if (0 == s_reg.active_phases) {
/* Automated creation not specified in header.
+
fprintf(stderr, "%s:%i: Sensor %s has no active_phases\n", __FILE__, __LINE__, s_reg.name);
// File doesn't exist.
 
tmp = access(file_name, F_OK);
 
if (tmp) {
 
*p = START;
 
return write_phase(*p);
 
}
 
*/
 
 
 
file = fopen(PERSISTENCE_PATH, "r");
 
if (NULL == file) {
 
fprintf(stderr, "%s:%i: opening persistence file for reading failed: %s\n", __FILE__, __LINE__, strerror(errno));
 
 
return EXIT_FAILURE;
 
return EXIT_FAILURE;
 
}
 
}
  
errno = 0;
+
// Allocate measurement
tmp = fscanf(file, "%u", p);
+
if (EXIT_FAILURE == measurement_create(&(s->measurements), s_reg.date_amt, s_reg.types, s_reg.priorities)) {
if (1 != tmp) {
+
fprintf(stderr, "%s:%i: measurement_create failed\n", __FILE__, __LINE__);
fprintf(stderr, "%s:%i: scanning for phase failed: %s\n", __FILE__, __LINE__, 0 == errno ? "no value" : strerror(errno));
 
rv = EXIT_FAILURE;
 
}
 
 
 
tmp = fclose(file);
 
if (tmp) {
 
fprintf(stderr, "%s:%i: closing persistence file after reading failed: %s\n", __FILE__, __LINE__, strerror(errno));
 
 
return EXIT_FAILURE;
 
return EXIT_FAILURE;
 
}
 
}
  
return rv;
+
return EXIT_SUCCESS;
 
}
 
}
  
  
int write_phase(phase p) {
+
int sensor_init(const i2c_sensor_reg *const s_reg, i2c_sensor *s) {
FILE *file = NULL;
+
if (NULL == s_reg) {
int tmp = 0;
+
fprintf(stderr, "%s:%i: Input s_reg is NULL\n", __FILE__, __LINE__);
 
 
file = fopen(PERSISTENCE_PATH, "w");
 
if (NULL == file) {
 
fprintf(stderr, "%s:%i: opening persistence file for writing failed: %s\n", __FILE__, __LINE__, strerror(errno));
 
 
return EXIT_FAILURE;
 
return EXIT_FAILURE;
 
}
 
}
 
+
if (NULL == s) {
tmp = fprintf(file, "%i", p);
+
fprintf(stderr, "%s:%i: Input s is NULL\n", __FILE__, __LINE__);
if (0 >= tmp) {
 
fprintf(stderr, "%s:%i: printing phase failed\n", __FILE__, __LINE__);
 
 
return EXIT_FAILURE;
 
return EXIT_FAILURE;
 
}
 
}
  
tmp = fclose(file);
+
s->t = NULL;
if (tmp) {
+
sensor_reset(s_reg, s);
fprintf(stderr, "%s:%i: closing persistence file after writing failed: %s\n", __FILE__, __LINE__, strerror(errno));
+
s->err_seq = 0;
return EXIT_FAILURE;
+
s->err_total = 0;
}
+
s->failure_total = 0;
  
 
return EXIT_SUCCESS;
 
return EXIT_SUCCESS;
Zeile 2.314: Zeile 2.122:
  
  
 +
int sensor_reset(const i2c_sensor_reg *const s_reg, i2c_sensor *s) {
 +
if (NULL == s_reg) {
 +
fprintf(stderr, "%s:%i: Input s_reg is NULL\n", __FILE__, __LINE__);
 +
return EXIT_FAILURE;
 +
}
 +
if (NULL == s) {
 +
fprintf(stderr, "%s:%i: Input s is NULL\n", __FILE__, __LINE__);
 +
return EXIT_FAILURE;
 +
}
 +
 +
s->reg = s_reg;
 +
s->state = INIT;
 +
s->it_rem = -1; // Default value, so sensor can recognize state entry
 +
s->processing_time = 0;
 +
s->csv_file = 0;
  
void sh_maintenance(int sig) {
+
return EXIT_SUCCESS;
unsigned long total_failures = 0;
+
}
size_t in_failure = 0;
 
  
size_t i_all = 0;
 
  
if (MAINTENANCE_SIGNAL == sig) {
+
int sensor_create_init_connected_sensors(const i2c_sensor_reg *const s_reg, size_t amt_s_reg, i2c_sensor **s_con, size_t *amt_con) {
// Collect combined metrics.
+
int rv = EXIT_SUCCESS;
for (size_t i = 0; i < s_con_amt; ++i) {
+
char *connected;
total_failures += s_con[i].failure_total;
+
size_t s_con_pos;
if (FAILURE == s_con[i].state) {
+
size_t cnt_failed;
++in_failure;
+
 
}
+
if (NULL == s_reg) {
 +
fprintf(stderr, "%s:%i: Input s is NULL\n", __FILE__, __LINE__);
 +
return EXIT_FAILURE;
 +
}
 +
if (0 == amt_s_reg) {
 +
fprintf(stderr, "%s:%i: Input amt is 0\n", __FILE__, __LINE__);
 +
return EXIT_FAILURE;
 +
}
 +
if (NULL == s_con) {
 +
fprintf(stderr, "%s:%i: Input s_con is NULL\n", __FILE__, __LINE__);
 +
return EXIT_FAILURE;
 +
}
 +
if (NULL == amt_con) {
 +
fprintf(stderr, "%s:%i: Input amt_con is NULL\n", __FILE__, __LINE__);
 +
return EXIT_FAILURE;
 +
}
 +
 
 +
// Determine size and position of connected sensors
 +
*amt_con = 0;
 +
errno = 0;
 +
connected = calloc(amt_s_reg, sizeof (char));
 +
if (NULL == connected && ENOMEM == errno) {
 +
fprintf(stderr, "%s:%i: calloc failed: %s\n", __FILE__, __LINE__, strerror(errno));
 +
return EXIT_FAILURE;
 +
}
 +
for (size_t i = 0; amt_s_reg > i; i++) {
 +
if (EXIT_SUCCESS == s_reg[i].is_connected()) {
 +
connected[i] = 1;
 +
(*amt_con)++;
 
}
 
}
 +
}
  
// TODO-IMPL - Show master status as following:
+
// Allocate i2c_sensor_array and init every connected
// Master-Status CRITICAL, all height sensors in FAILURE, or no height sensor
+
errno = 0;
printf("\n[%s][%s] %-10s (", 0 == total_failures ? "\033[0;92m GOOD \033[0m" : (0 == in_failure ? "\033[0;93m OKAY \033[0m" : "\033[0;33m ALRM \033[0m"), 0 < s_con_amt ? "\033[0;36mACTIVE\033[0m" : "\033[0;30m DEAD \033[0m", "master");
+
*s_con = malloc((*amt_con) * sizeof (i2c_sensor));
switch (active_phase) {
+
if (NULL == (*s_con) && ENOMEM == errno) {
// Single phases.
+
fprintf(stderr, "%s:%i: malloc failed: %s\n", __FILE__, __LINE__, strerror(errno));
case START:
+
rv = EXIT_FAILURE;
printf("START");
+
goto ERR_PROBE_CONNECTED_SENSORS_1;
break;
+
}
case ASCEND:
 
printf("ASCEND");
 
break;
 
case DESCEND:
 
printf("DESCEND");
 
break;
 
case RECOVERY:
 
printf("RECOVERY");
 
break;
 
// Combined phases (name strings ending with '*').
 
case FLIGHT:
 
printf("FLIGHT*");
 
break;
 
default:
 
printf("\033[0;90m?active_phase=%u?\033[0m", active_phase);
 
}
 
printf(" phase, %zu/%zu sensors connected, %lu failures total, %zu sensors in FAILURE)\n\n", s_con_amt, SENSOR_AMT, total_failures, in_failure);
 
  
printf("[ COND ][  TASK  ][STATE ]    NAME    (ERROR STATISTICS)\n");
+
s_con_pos = 0;
for (size_t i = 0; i < s_con_amt; ++i) {
+
cnt_failed = 0;
printf("[%s][%s][%s] %-10s (%lu failures total, %lu errors total, %u erorrs in sequence)\n",
+
for (size_t i = 0; amt_s_reg > i; i++) {
0 == s_con[i].err_total ? "\033[0;92m GOOD \033[0m" : (FAILURE == s_con[i].state ? "\033[0;91m FAIL \033[0m" : (0 == s_con[i].err_seq ? "\033[0;33m OKAY \033[0m" : "\033[0;33mINTRUP\033[0m")),
+
if(connected[i]) {
RUNNING == s_con[i].t->state ? "\033[0;92m RUNNING \033[0m" : "\033[0;34m STOPPED \033[0m",
+
// Create
INIT == s_con[i].state ? "\033[0;95m INIT \033[0m" : (!s_con[i].reg->active_phases ? "\033[0;35mHYBERN\033[0m" : (s_con[i].reg->active_phases & active_phase ? "\033[0;36mACTIVE\033[0m" : "\033[0;34mASLEEP\033[0m")),
+
if (EXIT_FAILURE == sensor_create_sub(s_reg[i], &((*s_con)[s_con_pos]))) {
s_con[i].reg->name, s_con[i].failure_total, s_con[i].err_total, s_con[i].err_seq);
+
fprintf(stderr, "%s:%i: Warning: sensor_create_sub failed, therefore sensor was dropped\n", __FILE__, __LINE__);
}
+
cnt_failed++;
if (SENSOR_AMT > s_con_amt) {
+
continue;
for (size_t i_con = 0; SENSOR_AMT > i_all && i_con < s_con_amt; ++i_all) {
 
if (&SENSORS[i_all] == s_con[i_con].reg) {
 
++i_con;
 
} else {
 
printf("[\033[0;30mDISCON\033[0m][      ] %-10s\n", SENSORS[i_all].name);
 
}
 
 
}
 
}
for (; SENSOR_AMT > i_all; ++i_all) {
+
// Init
printf("[\033[0;30mDISCON\033[0m][      ] %-10s\n", SENSORS[i_all].name);
+
if (EXIT_FAILURE == sensor_init(&(s_reg[i]), &((*s_con)[s_con_pos]))) {
 +
fprintf(stderr, "%s:%i: Warning: sensor_init failed, therefore sensor was dropped\n", __FILE__, __LINE__);
 +
cnt_failed++;
 +
continue;
 
}
 
}
 +
s_con_pos++;
 
}
 
}
 
fflush(stdout);
 
 
}
 
}
}
+
// If any sensor failed to create or init, try to reallocate if any sensor left
 
+
if (0 != cnt_failed) {
void sh_update_phase(int sig) {
+
(*amt_con) -= cnt_failed;
sigset_t all;
+
if (0 == (*amt_con)) {
sigset_t normal;
+
fprintf(stderr, "%s:%i: All connected sensors failed in sensor_create_sub\n", __FILE__, __LINE__);
int tmp = 0;
+
rv = EXIT_FAILURE;
 
+
goto ERR_PROBE_CONNECTED_SENSORS_1;
tmp = sigfillset(&all);
+
}
if (-1 == tmp) {
+
errno = 0;
fprintf(stderr, "%s:%i: filling blocking signal mask failed\n", __FILE__, __LINE__);
+
*s_con = realloc((*s_con), (*amt_con) * sizeof (i2c_sensor));
}
+
if (NULL == (*s_con) && ENOMEM == errno) {
tmp = sigprocmask(SIG_BLOCK, &all, &normal);
+
fprintf(stderr, "%s:%i: realloc failed: %s\n", __FILE__, __LINE__, strerror(errno));
if (-1 == tmp) {
+
rv = EXIT_FAILURE;
fprintf(stderr, "%s:%i: setting blocking signal mask failed\n", __FILE__, __LINE__);
+
goto ERR_PROBE_CONNECTED_SENSORS_2;
 +
}
 
}
 
}
  
if (UPDATE_SIGNAL == sig) {
+
if (EXIT_FAILURE == rv) {
tmp = read_phase(&active_phase);
+
ERR_PROBE_CONNECTED_SENSORS_2:
if (EXIT_FAILURE == tmp) {
+
for (int i = s_con_pos-1; 0 <= i; i--) {
fprintf(stderr, "%s:%i: reading phase on signal failed\n", __FILE__, __LINE__);
+
measurement_destroy(&((*s_con)[i].measurements));
}
 
tmp = task_update_states_by_phase(tasks, tasks_amt, active_phase);
 
if (EXIT_FAILURE == tmp) {
 
fprintf(stderr, "%s:%s:%i: task_update_states_by_phase failed\n", __FILE__, __func__, __LINE__);
 
 
}
 
}
 +
free(*s_con);
 
}
 
}
 +
ERR_PROBE_CONNECTED_SENSORS_1:
 +
free(connected);
  
tmp = sigprocmask(SIG_SETMASK, &normal, NULL);
+
return rv;
if (-1 == tmp) {
 
fprintf(stderr, "%s:%i: returning to normal signal mask failed\n", __FILE__, __LINE__);
 
}
 
 
}
 
}
  
  
int fallback_recovery_task(task *thiz, phase *cur_phase, struct timespec cur_time) {
+
void sensor_reg_print(i2c_sensor_reg reg) {
FILE *file = NULL;
+
printf("sensor_reg = { ");
int rv = EXIT_SUCCESS;
+
printf("name = %s; ", reg.name);
int rv_f = 0;
+
printf("active_phases = %d; ", reg.active_phases);
 +
printf("cycle_len = %ld; ", reg.cycle_len);
 +
for (size_t i = 0; reg.date_amt > i; i++) {
 +
printf("types[%zu] = %d; ", i, reg.types[i]);
 +
printf("priorities[%zu] = %d; ", i, reg.priorities[i]);
 +
}
 +
printf("csv_header = %s", reg.csv_header);
 +
printf(" i2c_freq_max = %d; ", reg.i2c_freq_max);
 +
printf("};");
 +
}
 +
 
  
if (NULL == thiz) {
+
void sensor_print(i2c_sensor s) {
fprintf(stderr, "%s:%s:%i: Input thiz is NULL\n", __FILE__, __func__, __LINE__);
+
printf("sensor = { ");
return EXIT_FAILURE;
+
if(NULL != s.t) {
 +
task_print(*(s.t));
 +
} else {
 +
printf("task = { NULL }");
 
}
 
}
if (NULL == cur_phase) {
+
printf(" reg = %p;", (const void *)s.reg);
fprintf(stderr, "%s:%s:%i: Input cur_phase is NULL\n", __FILE__, __func__, __LINE__);
+
printf(" state = %d;", s.state);
return EXIT_FAILURE;
+
printf(" it_rem = %d;", s.it_rem);
}
+
printf(" processing_time = %ld;", s.processing_time);
 
+
printf(" next_cycle_time.tv_sec = %ld;", s.next_cycle_time.tv_sec);
switch (*cur_phase) {
+
printf(" next_cycle_time.tv_nsec = %ld; ", s.next_cycle_time.tv_nsec);
case START: // Set next_time and save it
+
measurement_print(s.measurements);
// Set next_time
+
printf(" csv_file = %d; ", s.csv_file);
thiz->next_time.tv_sec = cur_time.tv_sec + MAX_FLIGHT_TIME_S;
+
printf(" err_seq = %d; ", s.err_seq);
thiz->next_time.tv_nsec = cur_time.tv_nsec;
+
printf(" err_total = %ld; ", s.err_total);
// Write next_time to file
+
printf(" failure_total = %ld; ", s.failure_total);
file = fopen(MAX_FLIGHT_TIME_PATH, "w");
+
if(NULL != s.reg) {
if (NULL == file) {
+
sensor_reg_print(*(s.reg));
fprintf(stderr, "%s:%s:%i: fopen failed: %s\n", __FILE__, __func__, __LINE__, strerror(errno));
+
} else {
return EXIT_FAILURE;
+
printf("reg = { NULL }");
}
+
}
rv_f = fprintf(file, "%ld\n%ld", thiz->next_time.tv_sec, thiz->next_time.tv_nsec);
+
printf("};");
if (0 >= rv_f) {
+
}
fprintf(stderr, "%s:%s:%i: fprintf failed and returned %d\n", __FILE__, __func__, __LINE__, rv_f);
 
rv = EXIT_FAILURE;
 
}
 
rv_f = fclose(file);
 
if (EOF == rv_f) {
 
fprintf(stderr, "%s:%s:%i: fclose failed: %s\n", __FILE__, __func__, __LINE__, strerror(errno));
 
return EXIT_FAILURE;
 
}
 
break;
 
  
case RECOVERY: // Stopp own task
 
thiz->state = STOPPED;
 
break;
 
  
default: // Check for passed MAX_FLIGHT_TIME
+
int sensor_has_date_type(i2c_sensor s, date_type type) {
// Is next_time not set?
+
for (size_t i = 0; s.reg->date_amt > i; i++) {
if (0 == thiz->next_time.tv_sec && 0 == thiz->next_time.tv_nsec) {
+
if (type == s.reg->types[i]) {
// Read next_time from file
+
return EXIT_SUCCESS;
file = fopen(MAX_FLIGHT_TIME_PATH, "r");
+
}
if (NULL == file) {
 
fprintf(stderr, "%s:%s:%i: fopen failed: %s\n", __FILE__, __func__, __LINE__, strerror(errno));
 
return EXIT_FAILURE;
 
}
 
rv_f = fscanf(file, "%ld\n%ld", &(thiz->next_time.tv_sec), &(thiz->next_time.tv_nsec));
 
errno = 0;
 
if (2 != rv_f) {
 
fprintf(stderr, "%s:%i: fscanf failed: %s\n", __FILE__, __LINE__, 0 == errno ? "faulty file content" : strerror(errno));
 
rv = EXIT_FAILURE;
 
}
 
rv_f = fclose(file);
 
if (EOF == rv_f) {
 
fprintf(stderr, "%s:%s:%i: fclose failed: %s\n", __FILE__, __func__, __LINE__, strerror(errno));
 
return EXIT_FAILURE;
 
}
 
}
 
// Is MAX_FLIGHT_TIME reached?
 
if (timespec_lt(thiz->next_time, cur_time)) {
 
fprintf(stderr, "%s:%s:%i: MAX_FLIGHT_TIME_S = %ld s reached. Switched state to RECOVERY.\n", __FILE__, __func__, __LINE__, MAX_FLIGHT_TIME_S);
 
*cur_phase = RECOVERY;
 
// Update phase
 
rv_f = task_update_states_by_phase(tasks, tasks_amt, active_phase);
 
if (EXIT_FAILURE == rv_f) {
 
fprintf(stderr, "%s:%s:%i: task_update_states_by_phase failed\n", __FILE__, __func__, __LINE__);
 
return EXIT_FAILURE;
 
}
 
rv_f = write_phase(*cur_phase);
 
if (EXIT_FAILURE == rv_f) {
 
fprintf(stderr, "%s:%s:%i: write_phase failed\n", __FILE__, __func__, __LINE__);
 
return EXIT_FAILURE;
 
}
 
// Stopp own task
 
thiz->state = STOPPED;
 
}
 
 
}
 
}
+
return EXIT_FAILURE;
return rv;
 
 
}
 
}
</syntaxhighlight>
+
 
 +
 
 +
int sensor_get_best_by_date_type(i2c_sensor *s, size_t amt, date_type type, i2c_sensor **sensor) {
 +
// Check input
 +
if (NULL == s) {
 +
fprintf(stderr, "%s:%i: s is NULL\n", __FILE__, __LINE__);
 +
return EXIT_FAILURE;
 +
}
 +
if (0 == amt) {
 +
fprintf(stderr, "%s:%i: amt is 0\n", __FILE__, __LINE__);
 +
return EXIT_FAILURE;
 +
}
 +
 
 +
*sensor = NULL;
 +
signed char prio = -128;
 +
for (size_t i = 0; amt > i; i++) {
 +
if (INIT == s[i].state || MEASURE == s[i].state) { // Sensor running?
 +
for (size_t j = 0; s[i].reg->date_amt > j; j++) { // Look into each date
 +
if ((type == s[i].reg->types[j]) && (prio < s[i].reg->priorities[j])) { // Right type and higher priority?
 +
*sensor = &(s[i]);
 +
prio = s[i].reg->priorities[j];
 +
}
 +
}
 +
// TODO-Check - For some reason, s[i].measurements.types[j] isn't stable
 +
// for (size_t j = 0; s[i].measurements.data_c > j; j++) { // Look into each measurement
 +
// if (type == s[i].measurements.types[j] && prio < s[i].measurements.priorities[j]) { // Right type and higher priority?
 +
// *sensor = &(s[i]);
 +
// prio = s[i].measurements.priorities[j];
 +
// }
 +
// }
 +
}
 +
}
 +
if (NULL == *sensor) {
 +
fprintf(stderr, "%s:%d: no matching sensor found\n", __FILE__, __LINE__);
 +
return EXIT_FAILURE;
 +
}
 +
return EXIT_SUCCESS;
 +
}
 +
 
 +
</syntaxhighlight>
 
</div>
 
</div>
 
</div>
 
</div>
  
 
<div class="mw-collapsible mw-collapsed" style="width:100%">
 
<div class="mw-collapsible mw-collapsed" style="width:100%">
; definitions.h  
+
;task.h
 
<div class="mw-collapsible-content">
 
<div class="mw-collapsible-content">
 
<syntaxhighlight lang="c">
 
<syntaxhighlight lang="c">
 +
 
/**
 
/**
  * @file  definitions.h
+
  * @file  task.h
  * @brief Definitions used by multiple header files
+
  * @brief Tasks for master program
 
  *
 
  *
 
  * @author Florian Schwarz
 
  * @author Florian Schwarz
 
  */
 
  */
  
#ifndef INCLUDE_DEFINITIONS_H_
+
#ifndef INCLUDE_TASK_H_
#define INCLUDE_DEFINITIONS_H_
+
#define INCLUDE_TASK_H_
 +
 
 +
#include <time.h>  // struct timespec
 +
 
 +
#include "definitions.h"  // phase
 +
 
  
 +
/**
 +
* @brief Task state
 +
*/
 +
typedef enum task_state {
 +
RUNNING,
 +
STOPPED
 +
} task_state;
  
 
/**
 
/**
  * @brief Possible operating phases of weather balloon as bit mask in chronological
+
  * @brief Task type
*        order.
 
 
  *
 
  *
  * Combine masks with bitwise OR ("|") and check for phase with bitwise AND
+
  * Used as switch argument in schedule loop.
* ("&"). The result of this is automatically usable as boolean value.\n
+
  * So except for SENSOR use only one task per type
* \n
 
  * Keeping a chronological order is important for being able to switch through
 
* the phases by just bit-shifting the active phase.
 
 
  */
 
  */
typedef enum phase {
+
typedef enum task_type {
START    = 0x01, ///< Pre-flight phase.
+
SENSOR,
ASCEND  = 0x02, ///< Ascending phase.
+
LED_MASTER,
DESCEND  = 0x04, ///< Descending phase.
+
LED_SENSORS,
RECOVERY = 0x08, ///< Recovery phase.
+
CAM_USB_TASK,
 
+
CAM_RASPI_TASK,
// Special phases.
+
GSM_TASK,
QUIT    = 0x10,   ///< Not an actual phase. Triggers program termination.
+
WATCHDOG,
 
+
WATCHPUPPY,
// Combined phases.
+
FALLBACK_STATE_TO_RECOVERY_CHANGER,
FLIGHT  = 0x06    ///< ASCEND | DESCEND
+
TASK_TYPE_AMT ///< Insert new tasks before this one
} phase;
+
} task_type;
 
 
#endif  // INCLUDE_DEFINITIONS_H_
 
 
 
 
 
 
 
</syntaxhighlight>
 
</div>
 
</div>
 
<div class="mw-collapsible mw-collapsed" style="width:100%">
 
; measurement.h
 
<div class="mw-collapsible-content">
 
<syntaxhighlight lang="c">
 
  
 
/**
 
/**
* @file  measurement.h
+
  * @brief Represents a task.
  * @brief Definitions for the Master programm.
 
 
  *
 
  *
  * @author Florian Schwarz, Jonas Gaida
+
  * Used for scheduling.
 
  */
 
  */
 +
typedef struct task {
 +
task_type type;
 +
task_state state; ///< sleeping wont get touched
 +
phase active_phases; ///<
 +
struct timespec next_time; ///< next execution time
 +
void *p; ///< Currently used to store pointer to sensor struct
 +
} task;
  
#ifndef INCLUDE_MEASUREMENTDATA_H_
+
/**
#define INCLUDE_MEASUREMENTDATA_H_
+
* @brief Prints info about task struct
 +
*
 +
* @param[in] t task struct to print
 +
*/
 +
void task_print(task t);
  
#include <stddef.h>  // size_t
 
#include <time.h>  // struct timespec
 
  
 +
/**
 +
* @brief Delivers next not STOPPED task.
 +
*
 +
* Task with smallest next_time and not STOPPED state is returned.
 +
* If there are multiple tasks with same next_time, the first one
 +
* is returned.
 +
*
 +
* @param[in] t Pointer to task array, which to search
 +
* @param[in] amt Size of task array t
 +
* @param[out] next Pointer to task with smallest next_time
 +
*
 +
* @return @c EXIT_SUCCESS if successful else @c EXIT_FAILURE.
 +
*/
 +
int task_get_next(task *t, size_t amt, task **next);
  
  
 
/**
 
/**
  * @brief Possible types of data stored in a measurement struct data field.
+
  * @brief Updates task state on all tasks dependening on their active_phases
 +
*
 +
* @param[in,out] tasks Pointer to task array, which to update
 +
* @param[in] amt Size of task array tasks
 +
* @param[in] cur_phase Current phase
 
  *
 
  *
  * @sa measurement
+
  * @return @c EXIT_SUCCESS if successful else @c EXIT_FAILURE.
 
  */
 
  */
typedef enum date_type {
+
int task_update_states_by_phase(task *tasks, size_t amt, phase cur_phase);
EXOTIC,            ///< Something else.
+
 
HEIGHT,            ///< Height in m (meter).
+
 
PRESSURE,           ///< Atmospheric pressure in hPa (hectopascal).
+
#endif  // INCLUDE_TASK_H_
TEMPERATURE,       ///< Temperature in °C (degree Celsius).
 
HUMIDITY,          ///< Relative humidity in % (percent).
 
ACCELERATION_X,    ///< Acceleration in m/s^2 (meter per second squared).
 
ACCELERATION_Y,    ///< Acceleration in m/s^2 (meter per second squared).
 
ACCELERATION_Z,    ///< Acceleration in m/s^2 (meter per second squared).
 
ROTATION_X,        ///< Rotation in °/s (degree per second).
 
ROTATION_Y,        ///< Rotation in °/s (degree per second).
 
ROTATION_Z,        ///< Rotation in °/s (degree per second).
 
MAGNETIC_FLUX_X,    ///< Magnetic flux density in T (Tesla).
 
MAGNETIC_FLUX_Y,    ///< Magnetic flux density in T (Tesla).
 
MAGNETIC_FLUX_Z,    ///< Magnetic flux density in T (Tesla).
 
IRRADIANCE,        ///< Irradiance of UV radiation in W/m^2 (Watt per square meter).
 
LUM_INTENSITY,      ///< Intensity of visible light in cd (candela).
 
POS_LAT,            ///< Latitude in DD (decimal degree).
 
POS_LON            ///< Longitute in DD (decimal degree).
 
} date_type;
 
  
 +
</syntaxhighlight>
 +
</div>
 +
</div>
 +
<div class="mw-collapsible mw-collapsed" style="width:100%">
 +
;task.c
 +
<div class="mw-collapsible-content">
 +
<syntaxhighlight lang="c">
 
/**
 
/**
  * @brief Interpreted measurements of a sensor of a specified kind.
+
* @file  task.c
 +
  * @brief Implementation of task.h
 +
*
 +
* @author Florian Schwarz
 
  */
 
  */
typedef struct measurement {
 
struct timespec time;        ///< Unix time when the data was recorded.
 
size_t          data_c;      ///< How many data fields exist.
 
date_type      *types;      ///< Which kind of data is stored in each field.
 
signed char    *priorities; ///< Priority for each field. May be used to select most accurat sensor for master, e.g. height determination. 0 default, the higher, the more prioritized
 
double          *data;      ///< Array of data fields.
 
} measurement;
 
  
 +
#include "task.h"
 +
 +
#include <stdlib.h>  // exit codes
 +
#include <stdio.h> // fprintf
 +
#include "timespec_helper.h" // timespec_lt
  
  
/**
+
void task_print(task t) {
* @brief Creates data array by allocating uninitialized memory and sets
+
printf("task = { ");
*        attributes. Priorities are set to 0.
+
printf("type = %d; ", t.type);
*
+
printf("state = %d; ", t.state);
* If @p data_c is zero, the method returns successfully and @c types and
+
printf("next_time.tv_sec = %ld; ", t.next_time.tv_sec);
* @c data are set to @c NULL. This is for other devices posing as sensors.
+
printf("next_time.tv_nsec = %ld; ", t.next_time.tv_nsec);
*
+
printf("p = 0x%p; ", t.p);
* @param[out] m      Pointer to an existing measurement struct.
+
printf("};");
* @param[in]  data_c Number of data fields.
+
}
* @param[in]  types  Types of data stored in @c m->data. Length is @p data_c.
 
* @param[in]  priorities Priorities for each measurement @c m->data. Length is @p data_c. If Null, all priorities will get set to 0.
 
*
 
* @return @c EXIT_SUCCESS if successful else @c EXIT_FAILURE.
 
*
 
* @sa measurement_destroy
 
*/
 
int measurement_create(measurement *const m, size_t data_c, const date_type *const types, const signed char *const priorities);
 
  
/**
 
* @brief Destroys data array by freeing memory and resets all attributes.
 
*
 
* If @c m.data_c is zero and @c m.data is @c NULL, nothing happens and the
 
* method returns successfully.
 
*
 
* @param[out] m Pointer to an existing measurement struct.
 
*
 
* @return @c EXIT_SUCCESS if successful else @c EXIT_FAILURE.
 
*
 
* @sa measurement_create
 
*/
 
int measurement_destroy(measurement *const m);
 
  
/**
+
int task_get_next(task * t, size_t amt, task ** next) {
* @brief Append a measurement as line in CSV format to an open, writable file.
+
if (NULL == t) {
*
+
fprintf(stderr, "%s:%i: Input t is NULL\n", __FILE__, __LINE__);
* @param[in] fd  Open file descriptor with write permission.
+
return EXIT_FAILURE;
* @param[in] m The measurement struct to be written.
+
}
*
+
if (0 == amt) {
* @return @c EXIT_SUCCESS if successful else @c EXIT_FAILURE.
+
fprintf(stderr, "%s:%i: Input amt is 0\n", __FILE__, __LINE__);
*/
+
return EXIT_FAILURE;
int measurement_append_to_csv_file(int fd, measurement m);
+
}
 +
if (NULL == next) {
 +
fprintf(stderr, "%s:%i: Input next is NULL\n", __FILE__, __LINE__);
 +
return EXIT_FAILURE;
 +
}
  
 +
// Search first not-stopped comparator task
 +
size_t i_next = amt;
 +
for (size_t i = 0; amt > i; i++) {
 +
if (STOPPED != t[i].state) {
 +
i_next = i;
 +
break;
 +
}
 +
}
 +
if (amt == i_next) {
 +
fprintf(stderr, "%s:%i: all tasks stopped\n", __FILE__, __LINE__);
 +
return EXIT_FAILURE;
 +
}
 +
 +
// Find smallest
 +
for (size_t i = i_next + 1; amt > i; i++) {
 +
if (STOPPED == t[i].state) {
 +
continue;
 +
}
 +
if (timespec_lt(t[i].next_time, t[i_next].next_time)) {
 +
i_next = i;
 +
}
 +
}
 +
*next = &(t[i_next]);
 +
 +
return EXIT_SUCCESS;
 +
}
  
/**
 
* @brief Prints info about measurement struct
 
*
 
* @param[in] m measurement struct to print
 
*/
 
void measurement_print(measurement m);
 
  
 +
int task_update_states_by_phase(task *tasks, size_t amt, phase cur_phase) {
 +
if (NULL == tasks) {
 +
fprintf(stderr, "%s:%s:%i: Input tasks is NULL\n", __FILE__, __func__, __LINE__);
 +
return EXIT_FAILURE;
 +
}
  
#endif  // INCLUDE_MEASUREMENTDATA_H_
+
for (size_t i = 0; amt > i; i++) {
 +
if (0 != (tasks[i].active_phases & cur_phase)) { // Active phase for task?
 +
tasks[i].state = RUNNING;
 +
} else {
 +
tasks[i].state = STOPPED;
 +
}
 +
}
 +
return EXIT_SUCCESS;
 +
}
  
 
</syntaxhighlight>
 
</syntaxhighlight>
Zeile 2.662: Zeile 2.524:
 
</div>
 
</div>
 
<div class="mw-collapsible mw-collapsed" style="width:100%">
 
<div class="mw-collapsible mw-collapsed" style="width:100%">
; measurement.c
+
;sensor_tmp117.h
 
<div class="mw-collapsible-content">
 
<div class="mw-collapsible-content">
 
<syntaxhighlight lang="c">
 
<syntaxhighlight lang="c">
 
 
/**
 
/**
  * @file  measurement.c
+
  * @file  sensor_tmp117.h
  * @brief Implementation of measurement.h
+
  * @brief Definitions for the TMP117 sensor.
 
  *
 
  *
  * @author Jonas Gaida, Florian Schwarz
+
  * @author Florian Schwarz
 
  */
 
  */
  
#include "measurement.h"
+
#ifndef INCLUDE_SENSORTMP117_H_
 
+
#define INCLUDE_SENSORTMP117_H_
#include <stdlib.h>  // exit codes
 
#include <unistd.h> // write
 
#include <stdio.h> // fprintf
 
#include <string.h>  // strerror, strlen, memcpy
 
#include <fcntl.h>  // fcntl
 
#include <errno.h>  // errno
 
  
 +
#include "sensor.h"
  
  
 
/*
 
/*
  * Local definitions of CSV symbols.
+
  * Implementations of the functions defined in sensor.h.
 
  */
 
  */
static const char TIME_SEP = '.';
+
int tmp117_init(phase p, signed char *const it_rem, long *const processing_time_rem);
static const char CSV_SEP = ';';
+
int tmp117_measure(phase p, measurement *const m, signed char *const it_rem, long *const processing_time_rem);
static const char CSV_NEWLINE = '\n';
+
int tmp117_is_connected(void);
  
 +
static const date_type TMP117_TYPES[] = {TEMPERATURE};
 +
static signed char TMP117_PRIORITIES[] = {1};
  
 +
static const size_t TMP117_DATE_AMT = sizeof TMP117_TYPES / sizeof (date_type);
  
 
/**
 
/**
  * @brief Local helper function for converting a measurement to a CSV line.
+
  * @brief Registration data for a TMP117 sensor.
*
 
* Does not validate arguments!
 
*
 
* @param[in] m The measurement to be converted.
 
*
 
* @return Newly allocated memory containing the line as null-terminated string.
 
*
 
* @sa measurement_append_csv_to_file
 
 
  */
 
  */
static char *measurement_to_csv(measurement m) {
+
static const i2c_sensor_reg TMP117 = {
int tmp_length = 0;
+
"TMP117",
 
+
START | FLIGHT,
size_t line_length = 1;  // Always has a null byte.
+
500,
size_t pos = 0;
+
TMP117_TYPES,
char *line = NULL;
+
TMP117_PRIORITIES,
 +
TMP117_DATE_AMT,
 +
"\"Unix Time (s)\";\"Temperature inside (°C)\"\n",
 +
BAUD_400KHZ,
 +
tmp117_init,
 +
tmp117_measure,
 +
tmp117_is_connected
 +
};
  
int status = EXIT_SUCCESS;
+
#endif  // INCLUDE_SENSORTMP117_H_
  
  
 +
</syntaxhighlight>
 +
</div>
 +
</div>
  
// Determine the length of the time as string.
+
<div class="mw-collapsible mw-collapsed" style="width:100%">
tmp_length = snprintf(NULL, 0, "%li%c%09li", m.time.tv_sec, TIME_SEP, m.time.tv_nsec);
+
;sensor_tmp117.c
if (0 > tmp_length) {
+
<div class="mw-collapsible-content">
fprintf(stderr, "%s:%i: determining time length failed\n", __FILE__, __LINE__);
+
<syntaxhighlight lang="c">
status = EXIT_FAILURE;
+
/**
goto ERR;
+
* @file  sensor_tmp117.c
}
+
* @brief Implementation of sensor_tmp117.h
line_length += (size_t) tmp_length;
+
*
 
+
* @author Florian Schwarz
for (size_t i = 0; m.data_c > i; ++i) {
+
*
tmp_length = snprintf(NULL, 0, "%lf", m.data[i]);
+
* some sensor specs:
if (0 > tmp_length) {
+
* - aimed range -55 °C to 125 °C (accuracy (min, typ, max): -0.25, +-0.1, 0.25)
fprintf(stderr, "%s:%i: determining data length failed: index %zu\n", __FILE__, __LINE__, i);
+
* - i2c addresses: 0x48 - 0x4B
status = EXIT_FAILURE;
+
* - data format: 16 Bit, MSB first
goto ERR;
+
* - temperature in two's complement, decimal place between bit 8 and 7
}
+
* - a "one-shot" measurement is triggered by writing 11 into MOD bits in configuration register
 +
*
 +
* some implementation decisions:
 +
* - ignored intergrated EEPROM for sensor intitialization for easier sensor displacement
 +
* - during power-on reset (POR) EEPROM is blocking configuration register for approx. 1.5 ms. This is handled by this implementation
 +
* - one-shot instead of continous mode chosen, because the latter isn't fitting to time requirements
 +
* - averaging disabled, because it needs at least 125 ms, which may get in conflict with requirements of minimum measurement frequency
 +
*
 +
*/
  
// Accumulate field lengths and add space for a CSV_SEP.
+
#include "sensor_tmp117.h"
line_length += 1 + (size_t) tmp_length;
 
}
 
  
// Add space for the final CSV_NEWLINE.
+
#include <stdlib.h>  // exit codes
++line_length;
+
#include <stdint.h>  // uint8_t, uint32_t
 +
#include <stdio.h>  // fprintf
 +
#include <string.h>  // strerror
 +
#include <time.h>  // clock_gettime
 +
#include <errno.h>  // errno
  
 +
#include "bcm2835.h"
  
  
// Allocate memory for the line buffer.
+
/*
line = calloc(line_length, sizeof (char));
+
* Important values only for this sensor.
if (NULL == line) {
+
*/
fprintf(stderr, "%s:%i: calloc of %zu bytes failed\n", __FILE__, __LINE__, line_length * sizeof (char));
+
static const uint8_t I2C_ADDR = 0x48;
status = EXIT_FAILURE;
+
static const char REG_ADDR_TEMP = 0x00;
goto ERR;
+
static const char REG_ADDR_CONF = 0x01;
}
+
static const char REG_ADDR_TEMP_OFFS = 0x07;
  
 +
static const uint8_t DATA_READY_BIT_POS = 13;
 +
static const uint8_t MOD_BIT_POS = 10;
 +
static const uint8_t AVG_BIT_POS = 5;
 +
static const uint16_t MOD_SHUTDOWN = 0x0001 << MOD_BIT_POS;
 +
static const uint16_t MOD_ONE_SHOT = 0x0003 << MOD_BIT_POS;
 +
static const uint16_t AVG_DISABLED = 0x0000 << AVG_BIT_POS;
  
 +
static const uint16_t CONF_REG = MOD_SHUTDOWN | AVG_DISABLED;
 +
static const uint16_t TEMP_OFFS_REG = 0x0000;
 +
 +
static const uint16_t EEPROM_BUSY_MASK = 0x1000;
  
// Print time first.
+
static const long EEPROM_BUSY_TIME= 2; // wait time in ms if EEPROM is blocking config registers
tmp_length = snprintf(line, line_length, "%li%c%09li", m.time.tv_sec, TIME_SEP, m.time.tv_nsec);
+
static const long ADC_TIME= 18;
if (0 > tmp_length) {
 
fprintf(stderr, "%s:%i: buffering time failed\n", __FILE__, __LINE__);
 
status = EXIT_FAILURE;
 
goto ERR_FREE;
 
}
 
pos = (size_t) tmp_length;
 
  
// Print each data point preceeded by a CSV_SEP.
+
static const uint32_t BUFFER_LENGTH = 3;
for (size_t i = 0; m.data_c > i; ++i) {
 
tmp_length = snprintf(&line[pos], line_length - pos, "%c%f", CSV_SEP, m.data[i]);
 
if (0 > tmp_length) {
 
fprintf(stderr, "%s:%i: buffering data failed: index %zu\n", __FILE__, __LINE__, i);
 
status = EXIT_FAILURE;
 
goto ERR_FREE;
 
}
 
pos += (size_t) tmp_length;
 
}
 
  
// Print final CSV_NEWLINE and closing null byte.
 
tmp_length = snprintf(&line[pos], line_length - pos, "%c", CSV_NEWLINE);
 
if (0 > tmp_length) {
 
fprintf(stderr, "%s:%i: buffering new line failed\n", __FILE__, __LINE__);
 
status = EXIT_FAILURE;
 
goto ERR_FREE;
 
}
 
  
ERR_FREE:
+
/*
if (EXIT_FAILURE == status) {
+
* 1. Check for blocked configuration register via EEPROM_Busy status flag
free(line);
+
* 2. Set configuration register with CONF_REG
line = NULL;
+
* 3. Set temperature offset register with TEMP_OFFS_REG
}
+
*/
 +
int tmp117_init(__attribute__((unused)) phase p, signed char *const it_rem, long *const processing_time_rem) {
  
ERR:
+
const uint8_t INIT_ITERATIONS = 2; // Need to be synced with switch case!
return line;
 
}
 
  
 +
(void) p;
 +
uint8_t rc;
 +
char buf[BUFFER_LENGTH];
 +
uint16_t reg;
  
int measurement_create(measurement *const m, size_t data_c, const date_type *const types, const signed char *const priorities) {
+
// Set it_rem if first entry
int rv = EXIT_SUCCESS;
+
if(0 > *it_rem) {
+
*it_rem = INIT_ITERATIONS-1;
if (NULL == m) {
 
fprintf(stderr, "%s:%i: no measurement given\n", __FILE__, __LINE__);
 
return EXIT_FAILURE;
 
}
 
if (NULL == types && data_c > 0) {
 
fprintf(stderr, "%s:%i: Input types is Null\n", __FILE__, __LINE__);
 
return EXIT_FAILURE;
 
}
 
if (NULL == priorities && data_c > 0) {
 
fprintf(stderr, "%s:%i: Input priorities is Null\n", __FILE__, __LINE__);
 
return EXIT_FAILURE;
 
 
}
 
}
  
m->types = calloc(data_c, sizeof (date_type));
+
bcm2835_i2c_setSlaveAddress(I2C_ADDR);
if (NULL == m->types && data_c > 0) {
+
 
fprintf(stderr, "%s:%i: calloc of %zu bytes failed\n", __FILE__, __LINE__, data_c * sizeof (date_type));
+
switch (*it_rem) {
return EXIT_FAILURE;
+
case 1:
}
+
// 1. Check for blocked configuration register via EEPROM_Busy status flag
m->priorities = calloc(data_c, sizeof (signed char));
 
if (NULL == m->priorities && data_c > 0) {
 
fprintf(stderr, "%s:%i: calloc of %zu bytes failed\n", __FILE__, __LINE__, data_c * sizeof (signed char));
 
rv = EXIT_FAILURE;
 
goto MEASURE_ERR_FREE_TYPES;
 
}
 
m->data = calloc(data_c, sizeof (double));
 
if (NULL == m->data && data_c > 0) {
 
fprintf(stderr, "%s:%i: calloc of %zu bytes failed\n", __FILE__, __LINE__, data_c * sizeof (double));
 
rv = EXIT_FAILURE;
 
goto MEASURE_ERR_FREE_PRIORITIES;
 
}
 
  
for (size_t i = 0; data_c > i; i++) {
+
// Read from configuration register
m->types[i] = types[i];
+
// Set register pointer
m->priorities[i] = priorities[i];
+
rc = bcm2835_i2c_write(&REG_ADDR_CONF, 1);
}
+
if (BCM2835_I2C_REASON_OK != rc) {
 +
fprintf(stderr, "%s:%i: writing target register %0#2hhx failed: reason code %0#2hhx\n", __FILE__, __LINE__, REG_ADDR_TEMP, rc);
 +
return EXIT_FAILURE;
 +
}
 +
// Read register
 +
rc = bcm2835_i2c_read(buf, 2);
 +
if (BCM2835_I2C_REASON_OK != rc) {
 +
fprintf(stderr, "%s:%i: reading from register %0#2hhx failed: reason code %0#2hhx\n", __FILE__, __LINE__, REG_ADDR_TEMP, rc);
 +
return EXIT_FAILURE;
 +
}
  
 +
reg = (buf[0] << 8) | buf[1];
  
if (EXIT_FAILURE == rv) {
+
// If EEPROM is blocking configuration register, wait for finishing
MEASURE_ERR_FREE_PRIORITIES:
+
if (1 == (reg & EEPROM_BUSY_MASK)) {
free(m->priorities);
+
*processing_time_rem = EEPROM_BUSY_TIME;
MEASURE_ERR_FREE_TYPES:
+
} else {
free(m->types);
+
*processing_time_rem = 0;
 +
}
 +
break;
  
}
+
case 0:
m->data_c = data_c;
 
  
return rv;
+
// 2. Set Configuration register with CONF_REG
}
 
  
int measurement_destroy(measurement *const m) {
+
buf[0] = REG_ADDR_CONF;
if (NULL == m) {
+
buf[1] = (CONF_REG >> 8) & 0xFF;
fprintf(stderr, "%s:%i: Input m is Null\n", __FILE__, __LINE__);
+
buf[2] = CONF_REG & 0XFF;
return EXIT_FAILURE;
+
// Set register pointer and write to register
}
+
rc = bcm2835_i2c_write(buf, 3);
 +
if (BCM2835_I2C_REASON_OK != rc) {
 +
fprintf(stderr, "%s:%i: writing target register %0#2hhx failed: reason code %0#2hhx\n", __FILE__, __LINE__, REG_ADDR_TEMP, rc);
 +
return EXIT_FAILURE;
 +
}
  
// Has no effect, if pointer is NULL.
+
// Read configuration register for validation
free(m->types);
+
rc = bcm2835_i2c_read(buf, 2);
free(m->data);
+
if (BCM2835_I2C_REASON_OK != rc) {
 +
fprintf(stderr, "%s:%i: reading from register %0#2hhx failed: reason code %0#2hhx\n", __FILE__, __LINE__, REG_ADDR_TEMP, rc);
 +
return EXIT_FAILURE;
 +
}
 +
reg = (buf[0] << 8) | buf[1];
 +
if (CONF_REG != reg) {
 +
fprintf(stderr, "%s:%d: configuration validation failed: read 0x%04x, but expected 0x%04x\n", __FILE__, __LINE__, reg, CONF_REG);
 +
return EXIT_FAILURE;
 +
}
  
// Reset attributes to avoid undefined behavior on later use.
+
// 3. Set temperature offset register with TEMP_OFFS_REG
m->data_c = 0;
+
buf[0] = REG_ADDR_TEMP_OFFS;
m->types = NULL;
+
buf[1] = (TEMP_OFFS_REG >> 8) & 0xFF;
m->data = NULL;
+
buf[2] = TEMP_OFFS_REG & 0XFF;
 +
// Set register pointer and write to register
 +
rc = bcm2835_i2c_write(buf, 3);
 +
if (BCM2835_I2C_REASON_OK != rc) {
 +
fprintf(stderr, "%s:%i: writing target register %0#2hhx failed: reason code %0#2hhx\n", __FILE__, __LINE__, REG_ADDR_TEMP, rc);
 +
return EXIT_FAILURE;
 +
}
 +
 
 +
// Read temperature offset register for validation
 +
rc = bcm2835_i2c_read(buf, 2);
 +
if (BCM2835_I2C_REASON_OK != rc) {
 +
fprintf(stderr, "%s:%i: reading from register %0#2hhx failed: reason code %0#2hhx\n", __FILE__, __LINE__, REG_ADDR_TEMP, rc);
 +
return EXIT_FAILURE;
 +
}
 +
reg = (buf[0] << 8) | buf[1];
 +
if (TEMP_OFFS_REG != reg) {
 +
fprintf(stderr, "%s:%d: temperature offset validation failed: read 0x%04x, but expected 0x%04x\n", __FILE__, __LINE__, reg, TEMP_OFFS_REG);
 +
return EXIT_FAILURE;
 +
}
 +
 
 +
*processing_time_rem = 0;
 +
break;
 +
}
  
 
return EXIT_SUCCESS;
 
return EXIT_SUCCESS;
 
}
 
}
  
int measurement_append_to_csv_file(int fd, measurement m) {
 
size_t line_length = 0;
 
char *line = NULL;
 
ssize_t written = 0;
 
  
int rv = EXIT_SUCCESS;
+
int tmp117_measure(__attribute__((unused)) phase p, measurement *const m, signed char *const it_rem, long *const processing_time_rem) {
 +
 
 +
const uint8_t MEASURE_ITERATIONS = 2; // Need to be synced with switch case!
  
if (-1 == fcntl(fd, F_GETFD)) {
+
char buf[BUFFER_LENGTH];
fprintf(stderr, "%s:%i: invalid file handle given\n", __FILE__, __LINE__);
+
int8_t rc;
rv = EXIT_FAILURE;
+
uint16_t reg;
goto ERR;
 
}
 
  
if (0 == m.data_c || NULL == m.data) {
+
if(0 > *it_rem) {
fprintf(stderr, "%s:%i: uninitialized measurement given\n", __FILE__, __LINE__);
+
*it_rem = MEASURE_ITERATIONS-1;
rv = EXIT_FAILURE;
 
goto ERR;
 
 
}
 
}
  
 +
bcm2835_i2c_setSlaveAddress(I2C_ADDR);
  
 +
switch (*it_rem) {
 +
case 1:
 +
// Acquire timestamp.
 +
if (0 > clock_gettime(CLOCK_REALTIME, &(m->time))) {
 +
fprintf(stderr, "%s:%d: acquiring measurement timestamp failed: %s\n", __FILE__, __LINE__, strerror(errno));
 +
return EXIT_FAILURE;
 +
}
  
// Generate CSV line.
+
// One-Shot Conversion
line = measurement_to_csv(m);
+
buf[0] = REG_ADDR_CONF;
if (NULL == line) {
+
buf[1] = (MOD_ONE_SHOT >> 8) & 0xFF;
fprintf(stderr, "%s:%i: generating CSV line failed\n", __FILE__, __LINE__);
+
buf[2] = MOD_ONE_SHOT & 0XFF;
rv = EXIT_FAILURE;
+
// Set register pointer and write to register
goto ERR;
+
rc = bcm2835_i2c_write(buf, 3);
}
+
if (BCM2835_I2C_REASON_OK != rc) {
line_length = strlen(line);
+
fprintf(stderr, "%s:%i: writing target register %0#2hhx failed: reason code %0#2hhx\n", __FILE__, __LINE__, REG_ADDR_TEMP, rc);
 +
return EXIT_FAILURE;
 +
}
  
// Write line to file.
+
*processing_time_rem = ADC_TIME;
written = write(fd, line, line_length);
+
break;
if (0 > written) {
+
 
fprintf(stderr, "%s:%d: writing CSV line failed: %s\n", __FILE__, __LINE__, strerror(errno));
+
case 0:
rv = EXIT_FAILURE;
+
// 1. Check for data_ready
goto ERR_FREE;
 
}
 
if (line_length != (size_t) written) {
 
fprintf(stderr, "%s:%d: writing CSV line failed: %zi/%zu bytes\n", __FILE__, __LINE__, written, line_length);
 
rv = EXIT_FAILURE;
 
goto ERR_FREE;
 
}
 
  
ERR_FREE:
+
// Read from configuration register
free(line);
+
// Set register pointer
 +
rc = bcm2835_i2c_write(&REG_ADDR_CONF, 1);
 +
if (BCM2835_I2C_REASON_OK != rc) {
 +
fprintf(stderr, "%s:%i: writing target register %0#2hhx failed: reason code %0#2hhx\n", __FILE__, __LINE__, REG_ADDR_TEMP, rc);
 +
return EXIT_FAILURE;
 +
}
 +
// Read register
 +
rc = bcm2835_i2c_read(buf, 2);
 +
if (BCM2835_I2C_REASON_OK != rc) {
 +
fprintf(stderr, "%s:%i: reading from register %0#2hhx failed: reason code %0#2hhx\n", __FILE__, __LINE__, REG_ADDR_TEMP, rc);
 +
return EXIT_FAILURE;
 +
}
  
ERR:
+
reg = (buf[0] << 8) | buf[1];
return rv;
+
if (0 == ((reg >> DATA_READY_BIT_POS) & 0x0001)) {
}
+
fprintf(stderr, "%s:%d: no temperature data ready\n", __FILE__, __LINE__);
 +
return EXIT_FAILURE;
 +
}
  
 +
// 2. Read from temperature register
  
void measurement_print(measurement m) {
+
// Set register pointer
printf("measurements = { ");
+
rc = bcm2835_i2c_write(&REG_ADDR_TEMP, 1);
printf("time.tv_sec = %ld; ", m.time.tv_sec);
+
if (BCM2835_I2C_REASON_OK != rc) {
printf("time.tv_nsec = %ld; ", m.time.tv_nsec);
+
fprintf(stderr, "%s:%i: writing target register %0#2hhx failed: reason code %0#2hhx\n", __FILE__, __LINE__, REG_ADDR_TEMP, rc);
printf("data_c = %zu; ", m.data_c);
+
return EXIT_FAILURE;
printf("data = { ");
+
}
for (size_t i = 0; m.data_c > i; i++) {
+
// Read register
printf("types[%zu] = %d; ", i, m.types[i]);
+
rc = bcm2835_i2c_read(buf, 2);
printf("priorities[%zu] = %d; ", i, m.priorities[i]);
+
if (BCM2835_I2C_REASON_OK != rc) {
printf("data[%zu] = %lf; ", i, m.data[i]);
+
fprintf(stderr, "%s:%i: reading from register %0#2hhx failed: reason code %0#2hhx\n", __FILE__, __LINE__, REG_ADDR_TEMP, rc);
 +
return EXIT_FAILURE;
 +
}
 +
 
 +
// 3. Convert data to Kelvin.
 +
 
 +
if (0x80 == (buf[0] & 0x80)) {
 +
// Negativ temperature
 +
buf[0] = buf[0] & 0x7F; // Clear SIGN
 +
m->data[0] = buf[0] * 2 + buf[1] / 128.0 - 256; // Shift bits to correct position
 +
}
 +
else {
 +
// Postive temperature
 +
m->data[0] = buf[0] * 2 + buf[1] / 128.0; // Shift bits to correct position
 +
}
 +
 
 +
*processing_time_rem = 0;
 +
break;
 
}
 
}
printf("}; ");
+
return EXIT_SUCCESS;
printf("};");
 
 
}
 
}
  
 +
 +
int tmp117_is_connected(void) {
 +
bcm2835_i2c_setSlaveAddress(I2C_ADDR);
 +
return bcm2835_i2c_write(NULL, 0);
 +
}
  
 
</syntaxhighlight>
 
</syntaxhighlight>
Zeile 2.932: Zeile 2.848:
  
 
<div class="mw-collapsible mw-collapsed" style="width:100%">
 
<div class="mw-collapsible mw-collapsed" style="width:100%">
;sensor.h
+
;camera_raspicam.h
 
<div class="mw-collapsible-content">
 
<div class="mw-collapsible-content">
 
<syntaxhighlight lang="c">
 
<syntaxhighlight lang="c">
 
 
/**
 
/**
  * @file  sensor.h
+
  * @file  camera_raspicam.h
  * @brief Definitions for all sensor programs.
+
  * @brief Definitions for the Raspi PI camera module
 
  *
 
  *
  * @author Jonas Gaida, Florian Schwarz
+
  * @author Arne Müller
 
  */
 
  */
  
#ifndef INCLUDE_SENSOR_H_
+
#ifndef INCLUDE_CAMERARASPICAM_H_
#define INCLUDE_SENSOR_H_
+
#define INCLUDE_CAMERARASPICAM_H_
  
#include <time.h>  // struct timespec
+
#include "camera.h"
  
#include "measurement.h"  // measurement_data
 
#include "task.h"  // task
 
#include "definitions.h"  // phase
 
  
 +
int raspicam_start(camera_mode mode, const char *filename, camera_parameters parameters);
 +
int raspicam_stop(void);
  
 
/**
 
/**
  * @brief Possible I2C bus frequencies.
+
  * @brief Initializer for a Raspberry PI camera moule
*
 
* Currently only values which are both within the official I2C specification
 
* and also part of the valid clock dividers in the bcm2835 library are
 
* available.
 
 
  */
 
  */
typedef enum baudrate {
+
#define RASPICAM {raspicam_start, raspicam_stop}
BAUD_100KHZ = 100000,    ///< The original mode at 100 kHz.
+
 
BAUD_400KHZ = 400000    ///< "Fast-mode" at 400 kHz.
+
#endif  // INCLUDE_CAMERARASPICAM_H_
  
// Additional frequencies from the I2C specification.
 
// BAUD_1000KHZ = 1000000,  ///< "Fast-mode plus" at 1 MHz.
 
// BAUD_3400KHZ = 3400000,  ///< "High-speed mode" at 3.4 MHz.
 
// BAUD_5000KHZ = 5000000,  ///< "Ultra Fast-mode" at 5 MHz (unidirectional).
 
  
// Additional frequencies from the bcm2835 library based on a 250 MHz clock.
+
</syntaxhighlight>
// BAUD_1666KHZ = 1666667,  ///< BCM2835_I2C_CLOCK_DIVIDER_150
+
</div>
// BAUD_1689KHZ = 1689189  ///< BCM2835_I2C_CLOCK_DIVIDER_148
+
</div>
} baudrate;
+
 
 +
<div class="mw-collapsible mw-collapsed" style="width:100%">
 +
;camera_raspicam.c
 +
<div class="mw-collapsible-content">
 +
<syntaxhighlight lang="c">
 +
/**
 +
* @file  camera_raspicam.c
 +
* @brief Implementation of camera_raspicam.h
 +
*
 +
* @author Arne Müller
 +
*/
 +
 
 +
#include "camera_raspicam.h"
  
#define MIN_BAUDRATE BAUD_100KHZ
+
#include <stdio.h>
#define MAX_BAUDRATE BAUD_400KHZ
+
#include <stdlib.h>
  
  
/**
 
* @brief Possible sensor states controlled by master
 
*/
 
typedef enum s_state {
 
UNKNOWN = 0x00,  ///< Default on-start state
 
UNREACHABLE,    ///< If first connection failed, never touch s again
 
INIT,            ///< If first connection success, init remaining or reconnected
 
MEASURE,        ///< If init finished, measure remaining or measure successful
 
SLEEP,          ///< If no active phase
 
FAILURE          ///< If consecutive failed communications > x, try is_connected all y seconds, if success, init
 
} s_state;
 
  
 +
static const unsigned char CAM_NUM = 0;
  
/**
+
static const unsigned char IMAGE_JPG_QUALITY = 95; // Must be between 0 and 100.
* @brief Sensor interface.
 
  */
 
typedef struct i2c_sensor_reg {
 
/**
 
* @brief Sensor name.
 
*
 
* The name is used for the creation of the data files, error messages and
 
* for easier identification by the user.
 
*/
 
const char *const name;
 
  
/**
+
static const unsigned long VIDEO_SEGMENT_MINUTES = 15;
* @brief Bit mask containing the phases during which the sensor is active.
+
static const double VIDEO_BITRATE_MB = 18.0;  // Must be below 25.
*/
 
const phase active_phases;
 
  
/**
+
static camera_mode cur_mode;
* @brief Length of a full measurement cycle of this sensor in milliseconds.
 
*
 
* Should not be shorter than sum of measure delays!
 
*/
 
const unsigned long cycle_len;
 
  
/**
 
* @brief Pointer to array of measurement types
 
*
 
* Needs to be in sync with measurement data
 
*/
 
const date_type *const types;
 
  
/**
 
* @brief Pointer to array of measurement priorities
 
*
 
* Needs to be in sync with measurement data
 
*/
 
const signed char *const priorities;
 
  
/**
+
/**
* @brief Amount of data packages to deliver
+
* Full documentation for the commands used: https://www.raspberrypi.org/documentation/raspbian/applications/camera.md
*
+
*/
* Needs to be in sync with measurement data
+
int raspicam_start(camera_mode mode, const char *filename, camera_parameters parameters) {
*/
+
 
const size_t date_amt;
+
char command[512];
 +
const size_t COMMAND_LEN = sizeof command / sizeof command[0];
 +
int tmp = -1;
  
/**
+
// Times are required in milliseconds.
* @brief Header line for CSV file (containing newline symbol) as
+
parameters.duration *= 1000;
*        null-terminated string.
+
parameters.frequency *= 1000;
*/
 
const char *const csv_header;
 
  
/**
+
cur_mode = mode;
* @brief The maximum frequency of the I2C bus supported by this sensor.
 
*/
 
const baudrate i2c_freq_max;
 
  
/**
+
if (PHOTO == mode) {
* @brief Initialization of the sensor.
 
*
 
* For sensors that need calibration or other initialization.
 
* This function is also called after leaving FAILURE or SLEEP state.
 
* So beware, if some initilizations are dependent of START state.
 
* (e.g. h_offset with launch location height and pressure.)
 
*/
 
int (*init)(phase p, signed char *const it_rem, long *const processing_time);
 
/**
 
* @brief Collecting measure data from sensor.
 
*
 
* Collection is finished if iterations_rem is <= 0.
 
* After state transition master sets it_rem < 0, so sensor can recognize
 
* first entry.
 
*
 
*/
 
int (*measure)(phase p, measurement *const m, signed char *const it_rem, long *const processing_time);
 
 
 
/**
 
* @brief I2C-Connectivity-Test to sensor, prior to an initializiation
 
*
 
* Is used in the beginning to determin connected sensors and to test in-flight
 
* failed sensors for reinitialization
 
*/
 
int (*is_connected)(void);
 
 
 
} i2c_sensor_reg;
 
  
 +
/* raspistill
 +
* --width      *          < Set the image width.
 +
* --height    *          < Set the image height.
 +
* --encoding  jpg        < Use the hardware-accelerated JPEG encoding.
 +
* --quality    *          < Set the JPEG image quality.
 +
* --exif      none      < Use no EXIF tags.
 +
* --timeout    *          < Set the duration of the image series (in milliseconds).
 +
* --timelapse  *          < Set the duration between single images in the series.
 +
* --camselect  *          < Set the recording camera in a multi-camera setup.
 +
* --framestart 0          < Set the number of the first image of the series (for use in the file name).
 +
* --nopreview            < Disable a live preview to a connected display.
 +
* --output    *_%05d.jpg < Set the filename of the images. A five digit index number gets appended.
 +
*/
 +
tmp = snprintf(command, COMMAND_LEN, "%s%hu%s%hu%s%hu%s%lu%s%lu%s%d%s%s%s",
 +
"raspistill --width ", parameters.width, " --height ", parameters.height,
 +
" --encoding jpg --quality ", IMAGE_JPG_QUALITY, " --exif none --timeout ", parameters.duration,
 +
" --timelapse ", parameters.frequency,
 +
" --camselect ", CAM_NUM,
 +
" --framestart 0 --nopreview --output ", filename, "_%05d.jpg &"
 +
);
 +
if (0 > tmp) {
 +
fprintf(stderr, "%s:%i: writing raspistill command to buffer failed\n", __FILE__, __LINE__);
 +
return EXIT_FAILURE;
 +
} else if (COMMAND_LEN <= (size_t) tmp) {
 +
fprintf(stderr, "%s:%i: writing raspistill command to buffer failed: %i bytes needed, got %zu\n", __FILE__, __LINE__, tmp, COMMAND_LEN);
 +
return EXIT_FAILURE;
 +
}
 +
} else if (VIDEO == mode) {
  
 +
/* raspivid
 +
* --width      *          < Set the image width.
 +
* --height    *          < Set the image height.
 +
* --bitrate    *          < Set the recording bitrate.
 +
* --framerate  *          < Set the recording framerate.
 +
* --intra      *          < Set the frequency of i-frames.
 +
* --codec      H264        < Use the hardware-accelerated H264 encoder.
 +
* --profile    high        < Use the "high" preset of the H264 encoder.
 +
* --level      4          < Use the encoding level 4 of the H264 encoder.
 +
* --inline                < Generate PPS and SPS headers for every i-frame.
 +
* --spstimings            < Insert timing information into SPS blocks.
 +
* --flush                  < Disable caching of video data after writing (decreases latency).
 +
* --timeout    *          < Set the duration of the recording (in milliseconds).
 +
* --camselect  *          < Set the recording camera in a multi-camera setup.
 +
* --segment    *          < Set the time after which the recording is split into another file (in milliseconds).
 +
* --start      0          < Set the number of the first segment of the recording (for use in the file name).
 +
* --nopreview              < Disable a live preview to a connected display.
 +
* --output    *_%05d.h264 < Set the filename of the recording. A five digit index number gets appended.
 +
*/
 +
tmp = snprintf(command, sizeof command / sizeof command[0], "%s%hu%s%hu%s%lu%s%hu%s%hu%s%lu%s%d%s%lu%s%s%s",
 +
"raspivid --width ", parameters.width, " --height ", parameters.height,
 +
" --bitrate ", (unsigned long) (VIDEO_BITRATE_MB * 1000000), " --framerate ", parameters.framerate,
 +
" --intra ", parameters.framerate,
 +
" --codec H264 --profile high --level 4 --inline --spstimings --flush --timeout ", parameters.duration,
 +
" --camselect ", CAM_NUM,
 +
" --segment ", VIDEO_SEGMENT_MINUTES * 60 * 1000, " --start 0 --nopreview --output ", filename, "_%H-%M-%S.h264 &"
 +
);
 +
if (0 > tmp) {
 +
fprintf(stderr, "%s:%i: writing raspivid command to buffer failed\n", __FILE__, __LINE__);
 +
return EXIT_FAILURE;
 +
} else if (COMMAND_LEN <= (size_t) tmp) {
 +
fprintf(stderr, "%s:%i: writing raspivid command to buffer failed: %i bytes needed, got %zu\n", __FILE__, __LINE__, tmp, COMMAND_LEN);
 +
return EXIT_FAILURE;
 +
}
 +
}
 +
 +
return system(command);
 +
}
  
/*
+
int raspicam_stop(void) {
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
if (PHOTO == cur_mode) {
* !!                                                                        !!
+
return system("killall raspistill");
* !!      REST OF FILE IS NOT PART OF INTERFACE BUT IS USED BY MASTER      !!
+
}
* !!                                                                        !!
+
return system("killall raspivid");
* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
}
*/
 
  
  
 +
</syntaxhighlight>
 +
</div>
 +
</div>
  
 +
<div class="mw-collapsible mw-collapsed" style="width:100%">
 +
;task_watchdog.h
 +
<div class="mw-collapsible-content">
 +
<syntaxhighlight lang="c">
 
/**
 
/**
  * @brief Sensor struct
+
* @file  task_watchdog.h
 +
  * @brief Definitions for the watchdog.
 +
*
 +
* @author Jonas Gaida
 
  */
 
  */
typedef struct i2c_sensor {
 
/**
 
* @brief Pointer to corresponding task // TODO-REVAL - Necessary?
 
*/
 
task *t;
 
/**
 
* @brief Pointer to original i2c_sensor_reg.
 
*/
 
const i2c_sensor_reg *reg;
 
/**
 
* @brief Sensor state.
 
*/
 
s_state state;
 
/**
 
* @brief Counter for multiple function calls (e.g. init or measure)
 
*
 
* Sensor function will get called additionally as often as this property is set.
 
* After state transition, this gets set to < 0, so sensor can recognize first
 
* entry. Then in first call sensor may set an aproporiate value
 
* (e.g. 2 for two further iterations, so with current in total 3 rounds).
 
* After leaving master checks if this is <= 0 and if true switches to next state.
 
* Decrement is handled by master.
 
*/
 
signed char it_rem;
 
/**
 
* @brief Time which the sensor needs to finish current action
 
*/
 
long processing_time;
 
/**
 
* @brief Time for a complete sensor cycle to redeem s_cycle_len.
 
*
 
* After finishing a full cycle s_cycle_len is added
 
*/
 
struct timespec next_cycle_time;
 
/**
 
* @brief Struct to fill with one measurement round
 
*/
 
measurement measurements;
 
/**
 
* @brief Destination file handler for csv output
 
*/
 
int csv_file;
 
/**
 
* @brief Sequential error counter
 
*/
 
unsigned char err_seq;
 
unsigned long err_total;
 
unsigned long failure_total;
 
 
} i2c_sensor;
 
  
 +
#ifndef INCLUDE_TASKWATCHDOG_H_
 +
#define INCLUDE_TASKWATCHDOG_H_
 +
 +
 +
 +
void wake_watchdog(void);
 +
 +
#endif  // INCLUDE_TASKWATCHDOG_H_
 +
 +
</syntaxhighlight>
 +
</div>
 +
</div>
 +
 +
<div class="mw-collapsible mw-collapsed" style="width:100%">
 +
;task_watchdog.c
 +
<div class="mw-collapsible-content">
 +
<syntaxhighlight lang="c">
 +
/**
 +
* @file  task_watchdog.c
 +
* @brief Implementation of task_watchdog.h
 +
*
 +
* @author Jonas Gaida
 +
*/
 +
 +
#include "task_watchdog.h"
 +
 +
#include "bcm2835.h"
 +
 +
 +
 +
//static const unsigned int PIN_SET0 = 26;
 +
//static const unsigned int PIN_SET1 = 19;
 +
static const unsigned int PIN_WDI = 16;
 +
 +
static unsigned char wdi_on = 0;
  
/**
 
* @brief Creates i2c_sensor
 
*
 
* Allocates necessary memory for i2c_sensor struct and sub structures
 
* specified by data from s_reg.
 
*
 
* @param[in] s_reg i2c_sensor_reg, wherefrom to take specification data
 
* @param[out] s Pointer to Pointer of i2c_sensor struct
 
*
 
* @return @c EXIT_SUCCESS if successful else @c EXIT_FAILURE.
 
*/
 
int sensor_create(const i2c_sensor_reg s_reg, i2c_sensor **s);
 
  
  
/**
+
void wake_watchdog(void) {
* @brief Creates only i2c_sensor substructures
+
// TODO: Pins are set to HIGH by default.
*
+
// Should configure 60 second intervals.
* If an array of i2c_sensors already exists, this function may come handy.
+
// bcm2835_gpio_set(PIN_SET0);
* Allocates necessary memory for i2c_sensor sub structures specified
+
// bcm2835_gpio_set(PIN_SET1);
* by data from s_reg.
 
*
 
* @param[in] s_reg i2c_sensor_reg with specifying data for allocation
 
* @param[out] s Pointer to i2c_sensor struct
 
*
 
* @return @c EXIT_SUCCESS if successful else @c EXIT_FAILURE.
 
*/
 
int sensor_create_sub(const i2c_sensor_reg s_reg, i2c_sensor *s);
 
  
 +
bcm2835_gpio_fsel(PIN_WDI, BCM2835_GPIO_FSEL_OUTP);
  
/**
+
wdi_on = !wdi_on;
* @brief Initialize i2c_sensor
+
if (wdi_on) {
*
+
bcm2835_gpio_set(PIN_WDI);
* Initializes i2c_sensor by data from s_reg.
+
} else {
*
+
bcm2835_gpio_clr(PIN_WDI);
* @param[in] s_reg i2c_sensor_reg, wherefrom to take init data
 
* @param[out] s Pointer to i2c_sensor struct
 
*
 
* @return @c EXIT_SUCCESS if successful else @c EXIT_FAILURE.
 
*/
 
int sensor_init(const i2c_sensor_reg *const s_reg, i2c_sensor *s);
 
 
 
 
 
/**
 
* @brief Initialize i2c_sensor, except task pointer and error values
 
*
 
* Initializes i2c_sensor by data from s_reg.
 
*
 
* @param[in] s_reg i2c_sensor_reg, wherefrom to take init data
 
* @param[out] s Pointer to i2c_sensor struct
 
*
 
* @return @c EXIT_SUCCESS if successful else @c EXIT_FAILURE.
 
*/
 
int sensor_reset(const i2c_sensor_reg *const s_reg, i2c_sensor *s);
 
 
 
 
 
/**
 
* @brief Probes in an i2c_sensor_reg array for connected sensors, allocates necessary recources and initializes them.
 
*
 
* To probe is_connected() is called for each sensor.
 
* Appropriate memory gets allocated for s_con and it gets
 
* initialized with given i2c_sensor_reg data.
 
*
 
* @param[in] s_reg Pointer to i2c_sensor_reg array, which to probe
 
* @param[in] amt_s_reg Size of i2c_sensor_reg array s
 
* @param[out] s_con Pointer to uninitialized i2c_sensor pointer
 
* @param[out] amt_con Pointer to size of i2c_sensor
 
*
 
* @return @c EXIT_SUCCESS if successful else @c EXIT_FAILURE.
 
*/
 
int sensor_create_init_connected_sensors(const i2c_sensor_reg *const s_reg, size_t amt_s_reg, i2c_sensor **s_con, size_t *amt_con);
 
 
 
 
 
/**
 
* @brief Prints info about sensor_reg
 
*
 
* @param[in] reg i2c_sensor to print
 
*/
 
void sensor_reg_print(i2c_sensor_reg reg);
 
 
 
 
 
/**
 
* @brief Prints info about sensor
 
*
 
* @param[in] s i2c_sensor to print
 
*/
 
void sensor_print(i2c_sensor s);
 
 
 
 
 
/**
 
* @brief Tests if sensor @p s provides measurements of type @p type
 
*
 
* @param[in] s i2c_sensor, which to search
 
* @param[in] type The wanted date_type
 
*
 
* @return @c EXIT_SUCCESS if successful else @c EXIT_FAILURE.
 
*/
 
int sensor_has_date_type(i2c_sensor s, date_type type);
 
 
 
 
 
/**
 
* @brief Delivers working sensor with highest priority on in type specified date_type
 
*
 
* If equal priorities, then first match is returned.
 
*
 
* @param[in] s Pointer to i2c_sensor array, which to search
 
* @param[in] amt Size of i2c_sensor array s
 
* @param[in] type The wanted date_type
 
* @param[out] sensor Pointer to working height sensor with highest priority
 
*
 
* @return @c EXIT_SUCCESS if successful else @c EXIT_FAILURE.
 
*/
 
int sensor_get_best_by_date_type(i2c_sensor *s, size_t amt, date_type type, i2c_sensor **sensor);
 
 
 
 
 
#endif  // INCLUDE_SENSOR_H_
 
 
 
</syntaxhighlight>
 
</div>
 
</div>
 
 
 
<div class="mw-collapsible mw-collapsed" style="width:100%">
 
;sensor.c
 
<div class="mw-collapsible-content">
 
<syntaxhighlight lang="c">
 
 
 
/**
 
* @file  sensor.c
 
* @brief Implementation of sensor.h
 
*
 
* @author Florian Schwarz
 
*/
 
 
 
#include "sensor.h"
 
 
 
#include <stdlib.h>  // exit codes
 
#include <stdio.h> // fprintf
 
#include <errno.h>  // errno
 
#include <string.h>  // strerror
 
 
 
int sensor_create(const i2c_sensor_reg s_reg, i2c_sensor **s) {
 
int rv = EXIT_SUCCESS;
 
int rv_f;
 
if (NULL == s) {
 
fprintf(stderr, "%s:%i: Input s is NULL\n", __FILE__, __LINE__);
 
return EXIT_FAILURE;
 
}
 
// Allocate i2c and let the rest sensor_create_sub() do
 
errno = 0;
 
*s = malloc(sizeof (i2c_sensor));
 
if (NULL == (*s) && ENOMEM == errno) {
 
fprintf(stderr, "%s:%i: malloc failed: %s\n", __FILE__, __LINE__, strerror(errno));
 
return EXIT_FAILURE;
 
}
 
rv_f = sensor_create_sub(s_reg, *s);
 
if (EXIT_FAILURE == rv_f) {
 
fprintf(stderr, "%s:%i: sensor_create_sub failed\n", __FILE__, __LINE__);
 
rv = EXIT_FAILURE;
 
 
 
}
 
 
 
if (EXIT_FAILURE == rv) {
 
free(*s);
 
}
 
return rv;
 
}
 
 
 
int sensor_create_sub(const i2c_sensor_reg s_reg, i2c_sensor *s) {
 
if (NULL == s) {
 
fprintf(stderr, "%s:%i: Input s is NULL\n", __FILE__, __LINE__);
 
return EXIT_FAILURE;
 
}
 
 
 
// Catch faulty registered sensors
 
 
 
if (0 == s_reg.cycle_len) {
 
fprintf(stderr, "%s:%i: Sensor %s cycle_len is 0\n", __FILE__, __LINE__, s_reg.name);
 
return EXIT_FAILURE;
 
}
 
if (0 == s_reg.active_phases) {
 
fprintf(stderr, "%s:%i: Sensor %s has no active_phases\n", __FILE__, __LINE__, s_reg.name);
 
return EXIT_FAILURE;
 
}
 
 
 
// Allocate measurement
 
if (EXIT_FAILURE == measurement_create(&(s->measurements), s_reg.date_amt, s_reg.types, s_reg.priorities)) {
 
fprintf(stderr, "%s:%i: measurement_create failed\n", __FILE__, __LINE__);
 
return EXIT_FAILURE;
 
}
 
 
 
return EXIT_SUCCESS;
 
}
 
 
 
 
 
int sensor_init(const i2c_sensor_reg *const s_reg, i2c_sensor *s) {
 
if (NULL == s_reg) {
 
fprintf(stderr, "%s:%i: Input s_reg is NULL\n", __FILE__, __LINE__);
 
return EXIT_FAILURE;
 
}
 
if (NULL == s) {
 
fprintf(stderr, "%s:%i: Input s is NULL\n", __FILE__, __LINE__);
 
return EXIT_FAILURE;
 
}
 
 
 
s->t = NULL;
 
sensor_reset(s_reg, s);
 
s->err_seq = 0;
 
s->err_total = 0;
 
s->failure_total = 0;
 
 
 
return EXIT_SUCCESS;
 
}
 
 
 
 
 
int sensor_reset(const i2c_sensor_reg *const s_reg, i2c_sensor *s) {
 
if (NULL == s_reg) {
 
fprintf(stderr, "%s:%i: Input s_reg is NULL\n", __FILE__, __LINE__);
 
return EXIT_FAILURE;
 
}
 
if (NULL == s) {
 
fprintf(stderr, "%s:%i: Input s is NULL\n", __FILE__, __LINE__);
 
return EXIT_FAILURE;
 
}
 
 
 
s->reg = s_reg;
 
s->state = INIT;
 
s->it_rem = -1; // Default value, so sensor can recognize state entry
 
s->processing_time = 0;
 
s->csv_file = 0;
 
 
 
return EXIT_SUCCESS;
 
}
 
 
 
 
 
int sensor_create_init_connected_sensors(const i2c_sensor_reg *const s_reg, size_t amt_s_reg, i2c_sensor **s_con, size_t *amt_con) {
 
int rv = EXIT_SUCCESS;
 
char *connected;
 
size_t s_con_pos;
 
size_t cnt_failed;
 
 
 
if (NULL == s_reg) {
 
fprintf(stderr, "%s:%i: Input s is NULL\n", __FILE__, __LINE__);
 
return EXIT_FAILURE;
 
}
 
if (0 == amt_s_reg) {
 
fprintf(stderr, "%s:%i: Input amt is 0\n", __FILE__, __LINE__);
 
return EXIT_FAILURE;
 
}
 
if (NULL == s_con) {
 
fprintf(stderr, "%s:%i: Input s_con is NULL\n", __FILE__, __LINE__);
 
return EXIT_FAILURE;
 
}
 
if (NULL == amt_con) {
 
fprintf(stderr, "%s:%i: Input amt_con is NULL\n", __FILE__, __LINE__);
 
return EXIT_FAILURE;
 
}
 
 
 
// Determine size and position of connected sensors
 
*amt_con = 0;
 
errno = 0;
 
connected = calloc(amt_s_reg, sizeof (char));
 
if (NULL == connected && ENOMEM == errno) {
 
fprintf(stderr, "%s:%i: calloc failed: %s\n", __FILE__, __LINE__, strerror(errno));
 
return EXIT_FAILURE;
 
}
 
for (size_t i = 0; amt_s_reg > i; i++) {
 
if (EXIT_SUCCESS == s_reg[i].is_connected()) {
 
connected[i] = 1;
 
(*amt_con)++;
 
}
 
}
 
 
 
// Allocate i2c_sensor_array and init every connected
 
errno = 0;
 
*s_con = malloc((*amt_con) * sizeof (i2c_sensor));
 
if (NULL == (*s_con) && ENOMEM == errno) {
 
fprintf(stderr, "%s:%i: malloc failed: %s\n", __FILE__, __LINE__, strerror(errno));
 
rv = EXIT_FAILURE;
 
goto ERR_PROBE_CONNECTED_SENSORS_1;
 
}
 
 
 
s_con_pos = 0;
 
cnt_failed = 0;
 
for (size_t i = 0; amt_s_reg > i; i++) {
 
if(connected[i]) {
 
// Create
 
if (EXIT_FAILURE == sensor_create_sub(s_reg[i], &((*s_con)[s_con_pos]))) {
 
fprintf(stderr, "%s:%i: Warning: sensor_create_sub failed, therefore sensor was dropped\n", __FILE__, __LINE__);
 
cnt_failed++;
 
continue;
 
}
 
// Init
 
if (EXIT_FAILURE == sensor_init(&(s_reg[i]), &((*s_con)[s_con_pos]))) {
 
fprintf(stderr, "%s:%i: Warning: sensor_init failed, therefore sensor was dropped\n", __FILE__, __LINE__);
 
cnt_failed++;
 
continue;
 
}
 
s_con_pos++;
 
}
 
}
 
// If any sensor failed to create or init, try to reallocate if any sensor left
 
if (0 != cnt_failed) {
 
(*amt_con) -= cnt_failed;
 
if (0 == (*amt_con)) {
 
fprintf(stderr, "%s:%i: All connected sensors failed in sensor_create_sub\n", __FILE__, __LINE__);
 
rv = EXIT_FAILURE;
 
goto ERR_PROBE_CONNECTED_SENSORS_1;
 
}
 
errno = 0;
 
*s_con = realloc((*s_con), (*amt_con) * sizeof (i2c_sensor));
 
if (NULL == (*s_con) && ENOMEM == errno) {
 
fprintf(stderr, "%s:%i: realloc failed: %s\n", __FILE__, __LINE__, strerror(errno));
 
rv = EXIT_FAILURE;
 
goto ERR_PROBE_CONNECTED_SENSORS_2;
 
}
 
}
 
 
 
if (EXIT_FAILURE == rv) {
 
ERR_PROBE_CONNECTED_SENSORS_2:
 
for (int i = s_con_pos-1; 0 <= i; i--) {
 
measurement_destroy(&((*s_con)[i].measurements));
 
}
 
free(*s_con);
 
}
 
ERR_PROBE_CONNECTED_SENSORS_1:
 
free(connected);
 
 
 
return rv;
 
}
 
 
 
 
 
void sensor_reg_print(i2c_sensor_reg reg) {
 
printf("sensor_reg = { ");
 
printf("name = %s; ", reg.name);
 
printf("active_phases = %d; ", reg.active_phases);
 
printf("cycle_len = %ld; ", reg.cycle_len);
 
for (size_t i = 0; reg.date_amt > i; i++) {
 
printf("types[%zu] = %d; ", i, reg.types[i]);
 
printf("priorities[%zu] = %d; ", i, reg.priorities[i]);
 
}
 
printf("csv_header = %s", reg.csv_header);
 
printf(" i2c_freq_max = %d; ", reg.i2c_freq_max);
 
printf("};");
 
}
 
 
 
 
 
void sensor_print(i2c_sensor s) {
 
printf("sensor = { ");
 
if(NULL != s.t) {
 
task_print(*(s.t));
 
} else {
 
printf("task = { NULL }");
 
}
 
printf(" reg = %p;", (const void *)s.reg);
 
printf(" state = %d;", s.state);
 
printf(" it_rem = %d;", s.it_rem);
 
printf(" processing_time = %ld;", s.processing_time);
 
printf(" next_cycle_time.tv_sec = %ld;", s.next_cycle_time.tv_sec);
 
printf(" next_cycle_time.tv_nsec = %ld; ", s.next_cycle_time.tv_nsec);
 
measurement_print(s.measurements);
 
printf(" csv_file = %d; ", s.csv_file);
 
printf(" err_seq = %d; ", s.err_seq);
 
printf(" err_total = %ld; ", s.err_total);
 
printf(" failure_total = %ld; ", s.failure_total);
 
if(NULL != s.reg) {
 
sensor_reg_print(*(s.reg));
 
} else {
 
printf("reg = { NULL }");
 
}
 
printf("};");
 
}
 
 
 
 
 
int sensor_has_date_type(i2c_sensor s, date_type type) {
 
for (size_t i = 0; s.reg->date_amt > i; i++) {
 
if (type == s.reg->types[i]) {
 
return EXIT_SUCCESS;
 
}
 
}
 
return EXIT_FAILURE;
 
}
 
 
 
 
 
int sensor_get_best_by_date_type(i2c_sensor *s, size_t amt, date_type type, i2c_sensor **sensor) {
 
// Check input
 
if (NULL == s) {
 
fprintf(stderr, "%s:%i: s is NULL\n", __FILE__, __LINE__);
 
return EXIT_FAILURE;
 
}
 
if (0 == amt) {
 
fprintf(stderr, "%s:%i: amt is 0\n", __FILE__, __LINE__);
 
return EXIT_FAILURE;
 
}
 
 
 
*sensor = NULL;
 
signed char prio = -128;
 
for (size_t i = 0; amt > i; i++) {
 
if (INIT == s[i].state || MEASURE == s[i].state) { // Sensor running?
 
for (size_t j = 0; s[i].reg->date_amt > j; j++) { // Look into each date
 
if ((type == s[i].reg->types[j]) && (prio < s[i].reg->priorities[j])) { // Right type and higher priority?
 
*sensor = &(s[i]);
 
prio = s[i].reg->priorities[j];
 
}
 
}
 
// TODO-Check - For some reason, s[i].measurements.types[j] isn't stable
 
// for (size_t j = 0; s[i].measurements.data_c > j; j++) { // Look into each measurement
 
// if (type == s[i].measurements.types[j] && prio < s[i].measurements.priorities[j]) { // Right type and higher priority?
 
// *sensor = &(s[i]);
 
// prio = s[i].measurements.priorities[j];
 
// }
 
// }
 
}
 
}
 
if (NULL == *sensor) {
 
fprintf(stderr, "%s:%d: no matching sensor found\n", __FILE__, __LINE__);
 
return EXIT_FAILURE;
 
}
 
return EXIT_SUCCESS;
 
}
 
 
 
</syntaxhighlight>
 
</div>
 
</div>
 
 
 
<div class="mw-collapsible mw-collapsed" style="width:100%">
 
;task.h
 
<div class="mw-collapsible-content">
 
<syntaxhighlight lang="c">
 
 
 
/**
 
* @file  task.h
 
* @brief Tasks for master program
 
*
 
* @author Florian Schwarz
 
*/
 
 
 
#ifndef INCLUDE_TASK_H_
 
#define INCLUDE_TASK_H_
 
 
 
#include <time.h>  // struct timespec
 
 
 
#include "definitions.h"  // phase
 
 
 
 
 
/**
 
* @brief Task state
 
*/
 
typedef enum task_state {
 
RUNNING,
 
STOPPED
 
} task_state;
 
 
 
/**
 
* @brief Task type
 
*
 
* Used as switch argument in schedule loop.
 
* So except for SENSOR use only one task per type
 
*/
 
typedef enum task_type {
 
SENSOR,
 
LED_MASTER,
 
LED_SENSORS,
 
CAM_USB_TASK,
 
CAM_RASPI_TASK,
 
GSM_TASK,
 
WATCHDOG,
 
WATCHPUPPY,
 
FALLBACK_STATE_TO_RECOVERY_CHANGER,
 
TASK_TYPE_AMT ///< Insert new tasks before this one
 
} task_type;
 
 
 
/**
 
* @brief Represents a task.
 
*
 
* Used for scheduling.
 
*/
 
typedef struct task {
 
task_type type;
 
task_state state; ///< sleeping wont get touched
 
phase active_phases; ///<
 
struct timespec next_time; ///< next execution time
 
void *p; ///< Currently used to store pointer to sensor struct
 
} task;
 
 
 
/**
 
* @brief Prints info about task struct
 
*
 
* @param[in] t task struct to print
 
*/
 
void task_print(task t);
 
 
 
 
 
/**
 
* @brief Delivers next not STOPPED task.
 
*
 
* Task with smallest next_time and not STOPPED state is returned.
 
* If there are multiple tasks with same next_time, the first one
 
* is returned.
 
*
 
* @param[in] t Pointer to task array, which to search
 
* @param[in] amt Size of task array t
 
* @param[out] next Pointer to task with smallest next_time
 
*
 
* @return @c EXIT_SUCCESS if successful else @c EXIT_FAILURE.
 
*/
 
int task_get_next(task *t, size_t amt, task **next);
 
 
 
 
 
/**
 
* @brief Updates task state on all tasks dependening on their active_phases
 
*
 
* @param[in,out] tasks Pointer to task array, which to update
 
* @param[in] amt Size of task array tasks
 
* @param[in] cur_phase Current phase
 
*
 
* @return @c EXIT_SUCCESS if successful else @c EXIT_FAILURE.
 
*/
 
int task_update_states_by_phase(task *tasks, size_t amt, phase cur_phase);
 
 
 
 
 
#endif  // INCLUDE_TASK_H_
 
 
 
</syntaxhighlight>
 
</div>
 
</div>
 
<div class="mw-collapsible mw-collapsed" style="width:100%">
 
;task.c
 
<div class="mw-collapsible-content">
 
<syntaxhighlight lang="c">
 
/**
 
* @file  task.c
 
* @brief Implementation of task.h
 
*
 
* @author Florian Schwarz
 
*/
 
 
 
#include "task.h"
 
 
 
#include <stdlib.h>  // exit codes
 
#include <stdio.h> // fprintf
 
#include "timespec_helper.h" // timespec_lt
 
 
 
 
 
void task_print(task t) {
 
printf("task = { ");
 
printf("type = %d; ", t.type);
 
printf("state = %d; ", t.state);
 
printf("next_time.tv_sec = %ld; ", t.next_time.tv_sec);
 
printf("next_time.tv_nsec = %ld; ", t.next_time.tv_nsec);
 
printf("p = 0x%p; ", t.p);
 
printf("};");
 
}
 
 
 
 
 
int task_get_next(task * t, size_t amt, task ** next) {
 
if (NULL == t) {
 
fprintf(stderr, "%s:%i: Input t is NULL\n", __FILE__, __LINE__);
 
return EXIT_FAILURE;
 
}
 
if (0 == amt) {
 
fprintf(stderr, "%s:%i: Input amt is 0\n", __FILE__, __LINE__);
 
return EXIT_FAILURE;
 
}
 
if (NULL == next) {
 
fprintf(stderr, "%s:%i: Input next is NULL\n", __FILE__, __LINE__);
 
return EXIT_FAILURE;
 
}
 
 
 
// Search first not-stopped comparator task
 
size_t i_next = amt;
 
for (size_t i = 0; amt > i; i++) {
 
if (STOPPED != t[i].state) {
 
i_next = i;
 
break;
 
}
 
}
 
if (amt == i_next) {
 
fprintf(stderr, "%s:%i: all tasks stopped\n", __FILE__, __LINE__);
 
return EXIT_FAILURE;
 
}
 
 
 
// Find smallest
 
for (size_t i = i_next + 1; amt > i; i++) {
 
if (STOPPED == t[i].state) {
 
continue;
 
}
 
if (timespec_lt(t[i].next_time, t[i_next].next_time)) {
 
i_next = i;
 
}
 
}
 
*next = &(t[i_next]);
 
 
 
return EXIT_SUCCESS;
 
}
 
 
 
 
 
int task_update_states_by_phase(task *tasks, size_t amt, phase cur_phase) {
 
if (NULL == tasks) {
 
fprintf(stderr, "%s:%s:%i: Input tasks is NULL\n", __FILE__, __func__, __LINE__);
 
return EXIT_FAILURE;
 
}
 
 
 
for (size_t i = 0; amt > i; i++) {
 
if (0 != (tasks[i].active_phases & cur_phase)) { // Active phase for task?
 
tasks[i].state = RUNNING;
 
} else {
 
tasks[i].state = STOPPED;
 
}
 
}
 
return EXIT_SUCCESS;
 
}
 
 
 
</syntaxhighlight>
 
</div>
 
</div>
 
<div class="mw-collapsible mw-collapsed" style="width:100%">
 
;sensor_tmp117.h
 
<div class="mw-collapsible-content">
 
<syntaxhighlight lang="c">
 
/**
 
* @file  sensor_tmp117.h
 
* @brief Definitions for the TMP117 sensor.
 
*
 
* @author Florian Schwarz
 
*/
 
 
 
#ifndef INCLUDE_SENSORTMP117_H_
 
#define INCLUDE_SENSORTMP117_H_
 
 
 
#include "sensor.h"
 
 
 
 
 
/*
 
* Implementations of the functions defined in sensor.h.
 
*/
 
int tmp117_init(phase p, signed char *const it_rem, long *const processing_time_rem);
 
int tmp117_measure(phase p, measurement *const m, signed char *const it_rem, long *const processing_time_rem);
 
int tmp117_is_connected(void);
 
 
 
static const date_type TMP117_TYPES[] = {TEMPERATURE};
 
static signed char TMP117_PRIORITIES[] = {1};
 
 
 
static const size_t TMP117_DATE_AMT = sizeof TMP117_TYPES / sizeof (date_type);
 
 
 
/**
 
* @brief Registration data for a TMP117 sensor.
 
*/
 
static const i2c_sensor_reg TMP117 = {
 
"TMP117",
 
START | FLIGHT,
 
500,
 
TMP117_TYPES,
 
TMP117_PRIORITIES,
 
TMP117_DATE_AMT,
 
"\"Unix Time (s)\";\"Temperature inside (°C)\"\n",
 
BAUD_400KHZ,
 
tmp117_init,
 
tmp117_measure,
 
tmp117_is_connected
 
};
 
 
 
#endif  // INCLUDE_SENSORTMP117_H_
 
 
 
 
 
</syntaxhighlight>
 
</div>
 
</div>
 
 
 
<div class="mw-collapsible mw-collapsed" style="width:100%">
 
;sensor_tmp117.c
 
<div class="mw-collapsible-content">
 
<syntaxhighlight lang="c">
 
/**
 
* @file  sensor_tmp117.c
 
* @brief Implementation of sensor_tmp117.h
 
*
 
* @author Florian Schwarz
 
*
 
* some sensor specs:
 
* - aimed range -55 °C to 125 °C (accuracy (min, typ, max): -0.25, +-0.1, 0.25)
 
* - i2c addresses: 0x48 - 0x4B
 
* - data format: 16 Bit, MSB first
 
* - temperature in two's complement, decimal place between bit 8 and 7
 
* - a "one-shot" measurement is triggered by writing 11 into MOD bits in configuration register
 
*
 
* some implementation decisions:
 
* - ignored intergrated EEPROM for sensor intitialization for easier sensor displacement
 
* - during power-on reset (POR) EEPROM is blocking configuration register for approx. 1.5 ms. This is handled by this implementation
 
* - one-shot instead of continous mode chosen, because the latter isn't fitting to time requirements
 
* - averaging disabled, because it needs at least 125 ms, which may get in conflict with requirements of minimum measurement frequency
 
*
 
*/
 
 
 
#include "sensor_tmp117.h"
 
 
 
#include <stdlib.h>  // exit codes
 
#include <stdint.h>  // uint8_t, uint32_t
 
#include <stdio.h>  // fprintf
 
#include <string.h>  // strerror
 
#include <time.h>  // clock_gettime
 
#include <errno.h>  // errno
 
 
 
#include "bcm2835.h"
 
 
 
 
 
/*
 
* Important values only for this sensor.
 
*/
 
static const uint8_t I2C_ADDR = 0x48;
 
static const char REG_ADDR_TEMP = 0x00;
 
static const char REG_ADDR_CONF = 0x01;
 
static const char REG_ADDR_TEMP_OFFS = 0x07;
 
 
 
static const uint8_t DATA_READY_BIT_POS = 13;
 
static const uint8_t MOD_BIT_POS = 10;
 
static const uint8_t AVG_BIT_POS = 5;
 
static const uint16_t MOD_SHUTDOWN = 0x0001 << MOD_BIT_POS;
 
static const uint16_t MOD_ONE_SHOT = 0x0003 << MOD_BIT_POS;
 
static const uint16_t AVG_DISABLED = 0x0000 << AVG_BIT_POS;
 
 
 
static const uint16_t CONF_REG = MOD_SHUTDOWN | AVG_DISABLED;
 
static const uint16_t TEMP_OFFS_REG = 0x0000;
 
 
 
static const uint16_t EEPROM_BUSY_MASK = 0x1000;
 
 
 
static const long EEPROM_BUSY_TIME= 2; // wait time in ms if EEPROM is blocking config registers
 
static const long ADC_TIME= 18;
 
 
 
static const uint32_t BUFFER_LENGTH = 3;
 
 
 
 
 
/*
 
* 1. Check for blocked configuration register via EEPROM_Busy status flag
 
* 2. Set configuration register with CONF_REG
 
* 3. Set temperature offset register with TEMP_OFFS_REG
 
*/
 
int tmp117_init(__attribute__((unused)) phase p, signed char *const it_rem, long *const processing_time_rem) {
 
 
 
const uint8_t INIT_ITERATIONS = 2; // Need to be synced with switch case!
 
 
 
(void) p;
 
uint8_t rc;
 
char buf[BUFFER_LENGTH];
 
uint16_t reg;
 
 
 
// Set it_rem if first entry
 
if(0 > *it_rem) {
 
*it_rem = INIT_ITERATIONS-1;
 
}
 
 
 
bcm2835_i2c_setSlaveAddress(I2C_ADDR);
 
 
 
switch (*it_rem) {
 
case 1:
 
// 1. Check for blocked configuration register via EEPROM_Busy status flag
 
 
 
// Read from configuration register
 
// Set register pointer
 
rc = bcm2835_i2c_write(&REG_ADDR_CONF, 1);
 
if (BCM2835_I2C_REASON_OK != rc) {
 
fprintf(stderr, "%s:%i: writing target register %0#2hhx failed: reason code %0#2hhx\n", __FILE__, __LINE__, REG_ADDR_TEMP, rc);
 
return EXIT_FAILURE;
 
}
 
// Read register
 
rc = bcm2835_i2c_read(buf, 2);
 
if (BCM2835_I2C_REASON_OK != rc) {
 
fprintf(stderr, "%s:%i: reading from register %0#2hhx failed: reason code %0#2hhx\n", __FILE__, __LINE__, REG_ADDR_TEMP, rc);
 
return EXIT_FAILURE;
 
}
 
 
 
reg = (buf[0] << 8) | buf[1];
 
 
 
// If EEPROM is blocking configuration register, wait for finishing
 
if (1 == (reg & EEPROM_BUSY_MASK)) {
 
*processing_time_rem = EEPROM_BUSY_TIME;
 
} else {
 
*processing_time_rem = 0;
 
}
 
break;
 
 
 
case 0:
 
 
 
// 2. Set Configuration register with CONF_REG
 
 
 
buf[0] = REG_ADDR_CONF;
 
buf[1] = (CONF_REG >> 8) & 0xFF;
 
buf[2] = CONF_REG & 0XFF;
 
// Set register pointer and write to register
 
rc = bcm2835_i2c_write(buf, 3);
 
if (BCM2835_I2C_REASON_OK != rc) {
 
fprintf(stderr, "%s:%i: writing target register %0#2hhx failed: reason code %0#2hhx\n", __FILE__, __LINE__, REG_ADDR_TEMP, rc);
 
return EXIT_FAILURE;
 
}
 
 
 
// Read configuration register for validation
 
rc = bcm2835_i2c_read(buf, 2);
 
if (BCM2835_I2C_REASON_OK != rc) {
 
fprintf(stderr, "%s:%i: reading from register %0#2hhx failed: reason code %0#2hhx\n", __FILE__, __LINE__, REG_ADDR_TEMP, rc);
 
return EXIT_FAILURE;
 
}
 
reg = (buf[0] << 8) | buf[1];
 
if (CONF_REG != reg) {
 
fprintf(stderr, "%s:%d: configuration validation failed: read 0x%04x, but expected 0x%04x\n", __FILE__, __LINE__, reg, CONF_REG);
 
return EXIT_FAILURE;
 
}
 
 
 
// 3. Set temperature offset register with TEMP_OFFS_REG
 
buf[0] = REG_ADDR_TEMP_OFFS;
 
buf[1] = (TEMP_OFFS_REG >> 8) & 0xFF;
 
buf[2] = TEMP_OFFS_REG & 0XFF;
 
// Set register pointer and write to register
 
rc = bcm2835_i2c_write(buf, 3);
 
if (BCM2835_I2C_REASON_OK != rc) {
 
fprintf(stderr, "%s:%i: writing target register %0#2hhx failed: reason code %0#2hhx\n", __FILE__, __LINE__, REG_ADDR_TEMP, rc);
 
return EXIT_FAILURE;
 
}
 
 
 
// Read temperature offset register for validation
 
rc = bcm2835_i2c_read(buf, 2);
 
if (BCM2835_I2C_REASON_OK != rc) {
 
fprintf(stderr, "%s:%i: reading from register %0#2hhx failed: reason code %0#2hhx\n", __FILE__, __LINE__, REG_ADDR_TEMP, rc);
 
return EXIT_FAILURE;
 
}
 
reg = (buf[0] << 8) | buf[1];
 
if (TEMP_OFFS_REG != reg) {
 
fprintf(stderr, "%s:%d: temperature offset validation failed: read 0x%04x, but expected 0x%04x\n", __FILE__, __LINE__, reg, TEMP_OFFS_REG);
 
return EXIT_FAILURE;
 
}
 
 
 
*processing_time_rem = 0;
 
break;
 
}
 
 
 
return EXIT_SUCCESS;
 
}
 
 
 
 
 
int tmp117_measure(__attribute__((unused)) phase p, measurement *const m, signed char *const it_rem, long *const processing_time_rem) {
 
 
 
const uint8_t MEASURE_ITERATIONS = 2; // Need to be synced with switch case!
 
 
 
char buf[BUFFER_LENGTH];
 
int8_t rc;
 
uint16_t reg;
 
 
 
if(0 > *it_rem) {
 
*it_rem = MEASURE_ITERATIONS-1;
 
}
 
 
 
bcm2835_i2c_setSlaveAddress(I2C_ADDR);
 
 
 
switch (*it_rem) {
 
case 1:
 
// Acquire timestamp.
 
if (0 > clock_gettime(CLOCK_REALTIME, &(m->time))) {
 
fprintf(stderr, "%s:%d: acquiring measurement timestamp failed: %s\n", __FILE__, __LINE__, strerror(errno));
 
return EXIT_FAILURE;
 
}
 
 
 
// One-Shot Conversion
 
buf[0] = REG_ADDR_CONF;
 
buf[1] = (MOD_ONE_SHOT >> 8) & 0xFF;
 
buf[2] = MOD_ONE_SHOT & 0XFF;
 
// Set register pointer and write to register
 
rc = bcm2835_i2c_write(buf, 3);
 
if (BCM2835_I2C_REASON_OK != rc) {
 
fprintf(stderr, "%s:%i: writing target register %0#2hhx failed: reason code %0#2hhx\n", __FILE__, __LINE__, REG_ADDR_TEMP, rc);
 
return EXIT_FAILURE;
 
}
 
 
 
*processing_time_rem = ADC_TIME;
 
break;
 
 
 
case 0:
 
// 1. Check for data_ready
 
 
 
// Read from configuration register
 
// Set register pointer
 
rc = bcm2835_i2c_write(&REG_ADDR_CONF, 1);
 
if (BCM2835_I2C_REASON_OK != rc) {
 
fprintf(stderr, "%s:%i: writing target register %0#2hhx failed: reason code %0#2hhx\n", __FILE__, __LINE__, REG_ADDR_TEMP, rc);
 
return EXIT_FAILURE;
 
}
 
// Read register
 
rc = bcm2835_i2c_read(buf, 2);
 
if (BCM2835_I2C_REASON_OK != rc) {
 
fprintf(stderr, "%s:%i: reading from register %0#2hhx failed: reason code %0#2hhx\n", __FILE__, __LINE__, REG_ADDR_TEMP, rc);
 
return EXIT_FAILURE;
 
}
 
 
 
reg = (buf[0] << 8) | buf[1];
 
if (0 == ((reg >> DATA_READY_BIT_POS) & 0x0001)) {
 
fprintf(stderr, "%s:%d: no temperature data ready\n", __FILE__, __LINE__);
 
return EXIT_FAILURE;
 
}
 
 
 
// 2. Read from temperature register
 
 
 
// Set register pointer
 
rc = bcm2835_i2c_write(&REG_ADDR_TEMP, 1);
 
if (BCM2835_I2C_REASON_OK != rc) {
 
fprintf(stderr, "%s:%i: writing target register %0#2hhx failed: reason code %0#2hhx\n", __FILE__, __LINE__, REG_ADDR_TEMP, rc);
 
return EXIT_FAILURE;
 
}
 
// Read register
 
rc = bcm2835_i2c_read(buf, 2);
 
if (BCM2835_I2C_REASON_OK != rc) {
 
fprintf(stderr, "%s:%i: reading from register %0#2hhx failed: reason code %0#2hhx\n", __FILE__, __LINE__, REG_ADDR_TEMP, rc);
 
return EXIT_FAILURE;
 
}
 
 
 
// 3. Convert data to Kelvin.
 
 
 
if (0x80 == (buf[0] & 0x80)) {
 
// Negativ temperature
 
buf[0] = buf[0] & 0x7F; // Clear SIGN
 
m->data[0] = buf[0] * 2 + buf[1] / 128.0 - 256; // Shift bits to correct position
 
}
 
else {
 
// Postive temperature
 
m->data[0] = buf[0] * 2 + buf[1] / 128.0; // Shift bits to correct position
 
}
 
 
 
*processing_time_rem = 0;
 
break;
 
}
 
return EXIT_SUCCESS;
 
}
 
 
 
 
 
int tmp117_is_connected(void) {
 
bcm2835_i2c_setSlaveAddress(I2C_ADDR);
 
return bcm2835_i2c_write(NULL, 0);
 
}
 
 
 
</syntaxhighlight>
 
</div>
 
</div>
 
 
 
<div class="mw-collapsible mw-collapsed" style="width:100%">
 
;camera_raspicam.h
 
<div class="mw-collapsible-content">
 
<syntaxhighlight lang="c">
 
/**
 
* @file  camera_raspicam.h
 
* @brief Definitions for the Raspi PI camera module
 
*
 
* @author Arne Müller
 
*/
 
 
 
#ifndef INCLUDE_CAMERARASPICAM_H_
 
#define INCLUDE_CAMERARASPICAM_H_
 
 
 
#include "camera.h"
 
 
 
 
 
int raspicam_start(camera_mode mode, const char *filename, camera_parameters parameters);
 
int raspicam_stop(void);
 
 
 
/**
 
* @brief Initializer for a Raspberry PI camera moule
 
*/
 
#define RASPICAM {raspicam_start, raspicam_stop}
 
 
 
#endif  // INCLUDE_CAMERARASPICAM_H_
 
 
 
 
 
</syntaxhighlight>
 
</div>
 
</div>
 
 
 
<div class="mw-collapsible mw-collapsed" style="width:100%">
 
;camera_raspicam.c
 
<div class="mw-collapsible-content">
 
<syntaxhighlight lang="c">
 
/**
 
* @file  camera_raspicam.c
 
* @brief Implementation of camera_raspicam.h
 
*
 
* @author Arne Müller
 
*/
 
 
 
#include "camera_raspicam.h"
 
 
 
#include <stdio.h>
 
#include <stdlib.h>
 
 
 
 
 
 
 
static const unsigned char CAM_NUM = 0;
 
 
 
static const unsigned char IMAGE_JPG_QUALITY = 95;  // Must be between 0 and 100.
 
 
 
static const unsigned long VIDEO_SEGMENT_MINUTES = 15;
 
static const double VIDEO_BITRATE_MB = 18.0;  // Must be below 25.
 
 
 
static camera_mode cur_mode;
 
 
 
 
 
 
 
/**
 
* Full documentation for the commands used: https://www.raspberrypi.org/documentation/raspbian/applications/camera.md
 
*/
 
int raspicam_start(camera_mode mode, const char *filename, camera_parameters parameters) {
 
 
 
char command[512];
 
const size_t COMMAND_LEN = sizeof command / sizeof command[0];
 
int tmp = -1;
 
 
 
// Times are required in milliseconds.
 
parameters.duration *= 1000;
 
parameters.frequency *= 1000;
 
 
 
cur_mode = mode;
 
 
 
if (PHOTO == mode) {
 
 
 
/* raspistill
 
* --width      *          < Set the image width.
 
* --height    *          < Set the image height.
 
* --encoding  jpg        < Use the hardware-accelerated JPEG encoding.
 
* --quality    *          < Set the JPEG image quality.
 
* --exif      none      < Use no EXIF tags.
 
* --timeout    *          < Set the duration of the image series (in milliseconds).
 
* --timelapse  *          < Set the duration between single images in the series.
 
* --camselect  *          < Set the recording camera in a multi-camera setup.
 
* --framestart 0          < Set the number of the first image of the series (for use in the file name).
 
* --nopreview            < Disable a live preview to a connected display.
 
* --output    *_%05d.jpg < Set the filename of the images. A five digit index number gets appended.
 
*/
 
tmp = snprintf(command, COMMAND_LEN, "%s%hu%s%hu%s%hu%s%lu%s%lu%s%d%s%s%s",
 
"raspistill --width ", parameters.width, " --height ", parameters.height,
 
" --encoding jpg --quality ", IMAGE_JPG_QUALITY, " --exif none --timeout ", parameters.duration,
 
" --timelapse ", parameters.frequency,
 
" --camselect ", CAM_NUM,
 
" --framestart 0 --nopreview --output ", filename, "_%05d.jpg &"
 
);
 
if (0 > tmp) {
 
fprintf(stderr, "%s:%i: writing raspistill command to buffer failed\n", __FILE__, __LINE__);
 
return EXIT_FAILURE;
 
} else if (COMMAND_LEN <= (size_t) tmp) {
 
fprintf(stderr, "%s:%i: writing raspistill command to buffer failed: %i bytes needed, got %zu\n", __FILE__, __LINE__, tmp, COMMAND_LEN);
 
return EXIT_FAILURE;
 
}
 
} else if (VIDEO == mode) {
 
 
 
/* raspivid
 
* --width      *          < Set the image width.
 
* --height    *          < Set the image height.
 
* --bitrate    *          < Set the recording bitrate.
 
* --framerate  *          < Set the recording framerate.
 
* --intra      *          < Set the frequency of i-frames.
 
* --codec      H264        < Use the hardware-accelerated H264 encoder.
 
* --profile    high        < Use the "high" preset of the H264 encoder.
 
* --level      4          < Use the encoding level 4 of the H264 encoder.
 
* --inline                < Generate PPS and SPS headers for every i-frame.
 
* --spstimings            < Insert timing information into SPS blocks.
 
* --flush                  < Disable caching of video data after writing (decreases latency).
 
* --timeout    *          < Set the duration of the recording (in milliseconds).
 
* --camselect  *          < Set the recording camera in a multi-camera setup.
 
* --segment    *          < Set the time after which the recording is split into another file (in milliseconds).
 
* --start      0          < Set the number of the first segment of the recording (for use in the file name).
 
* --nopreview              < Disable a live preview to a connected display.
 
* --output    *_%05d.h264 < Set the filename of the recording. A five digit index number gets appended.
 
*/
 
tmp = snprintf(command, sizeof command / sizeof command[0], "%s%hu%s%hu%s%lu%s%hu%s%hu%s%lu%s%d%s%lu%s%s%s",
 
"raspivid --width ", parameters.width, " --height ", parameters.height,
 
" --bitrate ", (unsigned long) (VIDEO_BITRATE_MB * 1000000), " --framerate ", parameters.framerate,
 
" --intra ", parameters.framerate,
 
" --codec H264 --profile high --level 4 --inline --spstimings --flush --timeout ", parameters.duration,
 
" --camselect ", CAM_NUM,
 
" --segment ", VIDEO_SEGMENT_MINUTES * 60 * 1000, " --start 0 --nopreview --output ", filename, "_%H-%M-%S.h264 &"
 
);
 
if (0 > tmp) {
 
fprintf(stderr, "%s:%i: writing raspivid command to buffer failed\n", __FILE__, __LINE__);
 
return EXIT_FAILURE;
 
} else if (COMMAND_LEN <= (size_t) tmp) {
 
fprintf(stderr, "%s:%i: writing raspivid command to buffer failed: %i bytes needed, got %zu\n", __FILE__, __LINE__, tmp, COMMAND_LEN);
 
return EXIT_FAILURE;
 
}
 
}
 
 
 
return system(command);
 
}
 
 
 
int raspicam_stop(void) {
 
if (PHOTO == cur_mode) {
 
return system("killall raspistill");
 
 
}
 
}
return system("killall raspivid");
 
 
}
 
}
 
  
 
</syntaxhighlight>
 
</syntaxhighlight>
Zeile 4.239: Zeile 3.074:
 
</div>
 
</div>
  
<div class="mw-collapsible mw-collapsed" style="width:100%">
+
Hierbei handelt es sich nur um Auszüge unserer Implementierung, um die Vorgehensweisen zu verdeutlichen. Der vollständige Quellcode ist unter [https://hbx.fhhrz.net/public?folderID=MjJ6cUZaWlphOVVLUTRXakMyNmVN  Quellcode] ersichtlich.
;task_watchdog.h
 
<div class="mw-collapsible-content">
 
<syntaxhighlight lang="c">
 
/**
 
* @file  task_watchdog.h
 
* @brief Definitions for the watchdog.
 
*
 
* @author Jonas Gaida
 
*/
 
 
 
#ifndef INCLUDE_TASKWATCHDOG_H_
 
#define INCLUDE_TASKWATCHDOG_H_
 
 
 
 
 
 
 
void wake_watchdog(void);
 
 
 
#endif  // INCLUDE_TASKWATCHDOG_H_
 
 
 
</syntaxhighlight>
 
</div>
 
</div>
 
 
 
<div class="mw-collapsible mw-collapsed" style="width:100%">
 
;task_watchdog.c
 
<div class="mw-collapsible-content">
 
<syntaxhighlight lang="c">
 
/**
 
* @file  task_watchdog.c
 
* @brief Implementation of task_watchdog.h
 
*
 
* @author Jonas Gaida
 
*/
 
 
 
#include "task_watchdog.h"
 
 
 
#include "bcm2835.h"
 
 
 
 
 
 
 
//static const unsigned int PIN_SET0 = 26;
 
//static const unsigned int PIN_SET1 = 19;
 
static const unsigned int PIN_WDI = 16;
 
 
 
static unsigned char wdi_on = 0;
 
 
 
 
 
 
 
void wake_watchdog(void) {
 
// TODO: Pins are set to HIGH by default.
 
// Should configure 60 second intervals.
 
// bcm2835_gpio_set(PIN_SET0);
 
// bcm2835_gpio_set(PIN_SET1);
 
  
bcm2835_gpio_fsel(PIN_WDI, BCM2835_GPIO_FSEL_OUTP);
+
== Real-Time-Clock ==
 
+
Die extra verbaute Real-Time-Clock funktionierte zunächst mit der vom Hersteller angegebenen Installations-Anleitung nicht. Nach etwas Recherche ließ sich das Problem darauf eingrenzen, dass der durch den Installer eingerichtete init-Service in neueren Raspbian-Versionen nicht mehr automatisch startet.
wdi_on = !wdi_on;
 
if (wdi_on) {
 
bcm2835_gpio_set(PIN_WDI);
 
} else {
 
bcm2835_gpio_clr(PIN_WDI);
 
}
 
}
 
  
</syntaxhighlight>
+
Zur Lösung wurde der Installer soweit abgeändert, dass ein Systemd-Service eingerichtet wird.
</div>
 
</div>
 
  
Hierbei handelt es sich nur um Auszüge unserer Implementierung, um die Vorgehensweisen zu verdeutlichen. Der vollständige Quellcode ist unter [https://hbx.fhhrz.net/public?folderID=MjJ6cUZaWlphOVVLUTRXakMyNmVN  Quellcode] ersichtlich.
+
Die gepatchte Version ist hier zu finden: [https://github.com/piface/PiFace-Real-Time-Clock/pull/19 Patch]
  
 
== Versenden einer SMS über den Pi ==
 
== Versenden einer SMS über den Pi ==

Aktuelle Version vom 18. März 2020, 18:50 Uhr

Formalia

Um einen einheitlichen Stil bei der Entwicklung einzuhalten, haben wir vorarb einen C-Styleguide aufgesetzt. Die Dokumentation erfolgt über Doxygen. Für die Speicherung der Messdaten haben wir uns für das CSV-Format entschieden, da dies einfach und schnell auszuwerten ist. Nachfolgend sind die jeweiligen Artikel zu finden.

C-Styleguide

CSV-Format

Doxygen

Implementierung

Als Implementierungssprache standen Python und C zur Auswahl. Unsere Wahl fiel auf C, da damit alle Gruppenmitglieder schon erste Erfahrungen sammeln konnten. Zur Ansteuerung der über I²C angeschlossenen Sensoren standen uns die Schnittstellen i2c-dev, WiringPi und die Registermanipulation mittels der bcm2835-Bibliothek zur Auswahl. Um die passende Methode zu finden, haben wir uns mit allen Varianten beschäftigt und die Vor- und Nachteile wie folgt zusammengefasst:

i2c-dev:
+ einfache Verwendung
- adapter_nr muss ermittelt werden
- Umweg über Filesystem -> schlechtere Performance

WiringPi:
+ sehr einfache Verwendung
o bis auf Setup, zu unflexible Funktionen (Alternative: Verwendung von write und read)
- Umweg über Filesystem -> schlechtere Performance

Low Level (Registermanipulation):
+ potentiell beste Performance
+ maximale Kontrolle
+ Unabhängigkeit von I2C-Bibliotheken -> bessere Portabilität bei identischem Prozessor auf unterschiedlichen Betriebssystemen
o root-only
- komplexer -> fehleranfälliger, zeitraubender, aber es gibt eine gute Vorlage zur Implementierung

Nach der Abwägung der Vor- und Nachteile haben wir uns für eine Implementierung mittels der bcm2835-Bibliothek entschieden, da uns die bessere Performance den größten Vorteil bei der Erfüllung der Anforderungen bot, auch wenn die Benutzerfreundlichkeit darunter litt.

Auszüge aus dem Quellcode

Nach dem in der Designphase festgeleten Mustern wurde der Master, die Sensoren sowie die Tasks wie folgt implementiert:

master.h
/**
 * @file  master.h
 * @brief Definitions for the Master program.
 *
 * @author Jonas Gaida, Florian Schwarz
 */

#ifndef INCLUDE_MASTER_H_
#define INCLUDE_MASTER_H_

#include "sensor.h"

#include "sensor_gyml8511.h"
#include "sensor_mcp9808.h"
#include "sensor_mpu9250.h"
#include "sensor_ms5611_out.h"
#include "sensor_ms8607_out.h"
#include "sensor_ms8607_in.h"
#include "sensor_sht31d_out.h"
#include "sensor_sht35_out.h"
#include "sensor_sht35_in.h"
#include "sensor_tmp117.h"
#include "sensor_camm8.h"

#include "sensor_height_sim.h" // Simulates flight height data

#include "sensor_buzzer.h"

#include "camera_raspicam.h"
#include "camera_usb.h"


/**
 * @brief Array of all registered sensors.
 *
 * This needs to be manually filled.
 */
const i2c_sensor_reg SENSORS[] = {
	MS8607_OUT,
	MS5611_OUT,
	MS8607_IN,
	SHT35_OUT,
	SHT31D_OUT,
	SHT35_IN,
	TMP117,
	MCP9808,
	MPU9250,
	GYML8511,
	CAMM8,
	HEIGHT_SIM,
	BUZZER
};


/**
 * @brief Constant for easy access to the total number of registered sensors.
 */
const size_t SENSOR_AMT = sizeof SENSORS / sizeof (i2c_sensor_reg);


/**
 * @brief Array of all cameras.
 *
 * This needs to be manually filled.
 */
const camera CAMERAS[] = {
	RASPICAM,
	USBCAM
};


/**
 * @brief Constant for easy access to the total number of cameras.
 */
const size_t CAMERA_AMT = sizeof CAMERAS / sizeof (camera);


/**
 * @brief Path of the persistence file for the phase.
 */
const char *const PERSISTENCE_PATH = "./phase";



/**
 * @brief The phase in which the master currently is.
 */
extern phase active_phase;



int main(void);


// Signal handler functions

/**
 * @brief Reads the phase saved in the file at PERSISTENCE_PATH.
 *
 * This method only reads if the file exists. Otherwise it does not create it
 * with a default phase. Instead it just returns unsuccessfully.
 *
 * @param[out] p Pointer to a phase which will be filled.
 *
 * @return @c EXIT_SUCCESS if successful else @c EXIT_FAILURE.
 *
 * @sa PERSISTENCE_PATH
 */
int read_phase(phase *const p);


/**
 * @brief Writes the given phase to the file at PERSISTENCE_PATH.
 *
 * @param[in] p The phase to be written.
 *
 * @return @c EXIT_SUCCESS if successful else @c EXIT_FAILURE.
 *
 * @sa PERSISTENCE_PATH
 */
int write_phase(phase p);


/**
 * @brief Checks the health running master.
 *
 * Implements the function type required by a signal handler.
 *
 * @param[in] sig The signal which triggered the handler call.
 */
void sh_maintenance(int sig);


/**
 * @brief Forces the active phase to be re-read.
 *
 * Implements the function type required by a signal handler.
 *
 * @param[in] sig The signal which triggered the handler call.
 *
 * @sa read_phase, active_phase
 */
void sh_update_phase(int sig);


/**
 * @brief Contains logic for the FALLBACK_STATE_TO_RECOVERY_CHANGER task
 *
 * Ensures that master switches to RECOVERY phase after MAX_FLIGHT_TIME.
 * To withstand forced reboot, this task writes its time for phase change to a file specified by MAX_FLIGHT_TIME_PATH
 *
 * @param[in,out] thiz pointer to own task struct
 * @param[in,out] cur_phase pointer to current active phase
 * @param[in] cur_time current time
 *
 */
int fallback_recovery_task(task *thiz, phase *cur_phase, struct timespec cur_time);
#endif  // INCLUDE_MASTER_H_
master.c
/**
 * @file  master.c
 * @brief Implementation of master.h
 *
 * @author Jonas Gaida, Florian Schwarz
 */

#include "master.h"

#include <stdlib.h>  // free, exit codes
#include <unistd.h>  // calloc
#include <stdio.h>  // fprintf, fflush, stdout, stderr
#include <fcntl.h>  // open
#include <sys/stat.h>  // open flags
#include <signal.h>  // struct sigaction, sigaction
#include <string.h>  // strerror, strlen
#include <time.h>  // clock_gettime
#include <limits.h>  // INT_MAX
#include <errno.h>  // errno

#include "bcm2835.h"
#include "timespec_helper.h"
#include "task.h"
#include "task_led.h"
#include "task_gsm.h"
#include "task_watchdog.h"



/**
 * @brief Signal which triggers the sh_maintenance function.
 *
 * @sa sh_maintenance
 */
static const int MAINTENANCE_SIGNAL = SIGUSR1;

/**
 * @brief Signal which triggers the interaction function.
 *
 * @sa interaction
 */
static const int UPDATE_SIGNAL = SIGUSR2;


/**
 * @brief Maximum time in s until state change to RECOVERY
 *
 * Fallback mechanism, to guarantee final RECOVERY mode.
 */
static const unsigned long MAX_FLIGHT_TIME_S = 3 * 60 * 60; // 3 hours
/**
 * @brief Stores time for phase change to RECOVERY
 */
const char *const MAX_FLIGHT_TIME_PATH = "./max_flight_time.pers";

/**
 * @brief Length of the ID suffix of each sensor's file name.
 *
 * This is used in a @c printf call as a '*' length field. These expect @c int
 * values. Therefor the value of this constant has to stay within @c int
 * boundaries.
 */
static const size_t FORMAT_FILE_ID_LENGTH = 3;

/**
 * @brief Number of milliseconds to wait after a failed init/measurement
 */
static const unsigned long ERR_SEQ_DELAY_MS = 50;

/**
 * @brief Number of milliseconds which each reconnection attempt to an sensor takes
 */
static const unsigned long ERR_PERIOD_MS = 3000;

/**
 * @brief Threshold for how many sequenced errors until sensors transition to failure state
 */
static const unsigned char ERR_THRESHOLD = 3;

/**
 * @brief Struct for connected sensors
 */
static task *tasks = NULL;

/**
 * @brief Struct for connected sensors
 */
static size_t tasks_amt = 0;

/**
 * @brief Struct for connected sensors
 */
static i2c_sensor *s_con = NULL;

/**
 * @brief Amount of actually connected sensors
 */
static size_t s_con_amt = 0;


// Defined as "extern" in master.h.
phase active_phase;


int main(void) {
	int rv = EXIT_SUCCESS; // return value for final exit
	//	int rv_f;  // return value of functions.
	//	int rv_l;  // return value for local sections

	uid_t uid = 0;  // For root check.
	baudrate baudrate = MAX_BAUDRATE;  // Should be highest by master supported I2C-frequency
	task *task = NULL;
	i2c_sensor *sensor = NULL, *sensor_height = NULL, *sensor_gps = NULL;
	size_t sensor_name_length = 0;
	size_t file_name_length = 0;
	char *file_name = NULL;
	ssize_t written = 0;  // For write() calls.
	struct timespec cur_time, diff_time;

	struct sigaction maintenance_action_default;
	struct sigaction update_action_default;
	struct sigaction maintenance_action;
	struct sigaction update_action;
	sigset_t all;
	// TODO-DEL - Signal-Blocking while sensors running not useful in case of program freezing
//	sigset_t normal; 

	const long WATCHDOG_KEEPALIVE_MS = 3000;
	const char *const WATCHPUPPY_SIGNALING_CMD = "killall -SIGUSR1 watchpuppy.sh";
	const long WATCHPUPPY_SIGNALING_CYCLE_S = 25; // needs to be smaller than TIMETOWAKEUP in watchpuppy.sh

	const double CYCLE_LEN_MULT = 1.0; // To globaly manipulate cycle_len

	// Constants for flight phase evaluation by height differences calculated by air pressure

	// Rough false value filtering
	const double HEIGHT_MIN = -500.0; // coast of dead sea
	const double HEIGHT_MAX = 50000.0; // balloon ascension limit + some puffer due to height calculation formular error in higher heights

	const unsigned char HEIGHT_AMT = 10; // Amount to collect, before calculate average vertical speed, false values included
	const double V_VELO_HALT_TO_FLIGHT_THRESHOLD = 0.5; // Threshold in m/s
	const uint16_t V_MOVEMENT_FILTER_MSK = 0xFFFF; // Bitmask to get last 16 Bits
	const uint16_t V_MOVEMENT_LANDED_MSK = 0x0000; // Bitmask to check for enough detected sequential non-movements
	const uint16_t  V_MOVEMENT_FLIGHING_MSK = V_MOVEMENT_FILTER_MSK; // Bitmask to check for enough detected sequential movements

	double height_last = 0.0; // Last height value
	double height_cur = 0.0; // Current height value
	double height_cnt = 0.0; // Counter for all height values, also false values
	double dheight_sum = 0.0; // Sum of delta height values

	double v_velo = 0.0; // vertical velocity in m/s
	uint16_t v_movement_seq = 0; // Bit counter for sequential vertical movement

	const long MASTER_LED_MS = 500;
	const long SENSORS_LED_MS = 100;
	int master_LED_on = 0;
	int sensors_LED_on = 0;


	// Camera configurations
	const camera_mode USB_CAMERA_MODE = PHOTO;
	const char *USB_CAMERA_FILENAME = "imgs/camera_usb_bottom";
	const camera_parameters USB_CAMERA_PARM = {
		0,
		10,    // freq, 1 picture per x seconds
//		1920, // width, too much CPU usage
//		1080, // height
//		1280, // width
//		720, // height
		640, // width
		480, // height
		0
	};
	char usb_camera_is_active = 0;

	const camera_mode RASPICAM_MODE = VIDEO;
	const char *RASPICAM_FILENAME = "vids/raspicam_side";
	const camera_parameters RASPICAM_PARM = {
		3 * 60 * 60,	  // duration, 3 hours, should be larger than flight time
		0,
		1920, // width
		1080, // height
		30    // framerate
	};
	char raspicam_is_active = 0;

	const long GSM_SEND_MS = 180000;  // Every 3 minutes.

	int tmp = 0;  // For unrelated use.

	// FLOW #1
	// Check for root privileges (bcm2835 doesn't do that automatically).
	if ((uid = getuid()) && geteuid() == uid)
	{
		errno = EPERM;
		fprintf(stderr, "%s:%i: root check failed: %s\n", __FILE__, __LINE__, strerror(errno));
		rv = EXIT_FAILURE;
		goto ERR;
	}

	// FLOW #2.1
	// Setup signal handling
	// Maintenance handler
	maintenance_action.sa_flags = 0;
	sigemptyset(&maintenance_action.sa_mask);
	sigaddset(&maintenance_action.sa_mask, MAINTENANCE_SIGNAL);
	maintenance_action.sa_handler = sh_maintenance;
	tmp = sigaction(MAINTENANCE_SIGNAL, &maintenance_action, &maintenance_action_default);
	if (0 > tmp) {
		fprintf(stderr, "%s:%d: setting maintenance handler failed\n", __FILE__, __LINE__);
		rv = EXIT_FAILURE;
		goto ERR;
	}
	// FLOW #2.2
	// Phase update handler
	update_action.sa_flags = 0;
	sigemptyset(&update_action.sa_mask);
	sigaddset(&update_action.sa_mask, UPDATE_SIGNAL);
	update_action.sa_handler = sh_update_phase;
	tmp = sigaction(UPDATE_SIGNAL, &update_action, &update_action_default);
	if (0 > tmp) {
		fprintf(stderr, "%s:%d: setting phase update handler failed\n", __FILE__, __LINE__);
		rv = EXIT_FAILURE;
		goto ERR_SIG_MAINT;
	}
	// Mask to block all signals
	tmp = sigfillset(&all);
	if (-1 == tmp) {
		fprintf(stderr, "%s:%i: filling blocking signal mask failed\n", __FILE__, __LINE__);
		rv = EXIT_FAILURE;
		goto ERR_SIG_UPDT;
	}

	// FLOW #3
	// Try reading a previously saved phase or instead use and write START.
	tmp = read_phase(&active_phase);
	if (EXIT_FAILURE == tmp) {
		active_phase = START;
		tmp = write_phase(active_phase);
		if (EXIT_FAILURE == tmp) {
			fprintf(stderr, "%s:%i: writing initial phase failed\n", __FILE__, __LINE__);
			rv = EXIT_FAILURE;
			goto ERR_SIG_UPDT;
		}
	}
	// FLOW #3.1
	// Update v_movement_seq
	if (FLIGHT == active_phase) {
		v_movement_seq = 0xFF;
	}

	// FLOW #4.1
	// bcm2835_init() comes with error message printing to stderr.
	if (!bcm2835_init()) {
		fprintf(stderr, "%s:%i: bcm2835_init failed\n", __FILE__, __LINE__);
		rv = EXIT_FAILURE;
		goto ERR_SIG_UPDT;
	}

	// FLOW #4.2
	// Initialize I2C functionality.
	if (!bcm2835_i2c_begin()) {
		fprintf(stderr, "%s:%i: I2C initialization failed\n", __FILE__, __LINE__);
		rv = EXIT_FAILURE;
		goto ERR_BCM_LIB_CLOSE;
	}

	// FLOW #5
	// Create and initialize array for connected sensors
	bcm2835_i2c_set_baudrate(MIN_BAUDRATE);
	if (EXIT_FAILURE == sensor_create_init_connected_sensors(SENSORS, SENSOR_AMT, &s_con, &s_con_amt)) {
		fprintf(stderr, "%s:%i: sensor_create_connected_sensors failed\n", __FILE__, __LINE__);
		rv = EXIT_FAILURE;
		goto ERR_BCM_I2C_END;
	}

	// FLOW #6
	// Determine greatest common I2C-frequency
	for (size_t i = 0; s_con_amt > i; i++) {
		if (NULL != s_con[i].reg) {
			if (baudrate > s_con[i].reg->i2c_freq_max) {
				baudrate = MIN_BAUDRATE;
			}
		}
	}
	bcm2835_i2c_set_baudrate(baudrate);

	// FLOW #7.1
	sensor_get_best_by_date_type(s_con, s_con_amt, HEIGHT, &sensor_height);
	if (NULL == sensor_height) {
		fprintf(stderr, "%s:%d: no height sensor found. terminate program.\n", __FILE__, __LINE__);
		rv = EXIT_FAILURE;
		goto ERR_S_CON_DESTROY;
	}

	// FLOW #7.2
	sensor_get_best_by_date_type(s_con, s_con_amt, POS_LAT, &sensor_gps);
	if (NULL == sensor_gps) {
		fprintf(stderr, "%s:%d: no GPS sensor found. Terminate program.\n", __FILE__, __LINE__);
		rv = EXIT_FAILURE;
		goto ERR_S_CON_DESTROY;
	}

	// FLOW #8.1
	// Find length of longest sensor name to allocate sufficient memory for file names.
	if (INT_MAX < FORMAT_FILE_ID_LENGTH) {
		fprintf(stderr, "%s:%i: FORMAT_FILE_ID_LENGTH too large: expected 0..%i, got %zu\n", __FILE__, __LINE__, INT_MAX, FORMAT_FILE_ID_LENGTH);
		rv = EXIT_FAILURE;
		goto ERR_S_CON_DESTROY;
	}

	for (size_t i = 0; s_con_amt > i; ++i) {
		sensor_name_length = strlen(s_con[i].reg->name);
		if (sensor_name_length > file_name_length) {
			file_name_length = sensor_name_length;
		}
	}
	file_name_length += FORMAT_FILE_ID_LENGTH + 6;  // Length of "_###.csv\0".
	file_name = calloc(file_name_length, sizeof (char));
	if (NULL == file_name) {
		fprintf(stderr, "%s:%i: calloc of %zu bytes failed\n", __FILE__, __LINE__, file_name_length * sizeof (char));
		rv = EXIT_FAILURE;
		goto ERR_S_CON_DESTROY;
	}

	// FLOW #8.2
	// CSV-Preperations
	for (size_t i = 0; s_con_amt > i; ++i) {
		// Prepare filenames.
		tmp = snprintf(file_name, file_name_length, "%s_%0*zu.csv", s_con[i].reg->name, (int) FORMAT_FILE_ID_LENGTH, i);
		if (0 > tmp) {
			fprintf(stderr, "%s:%i: buffering file name for %s failed\n", __FILE__, __LINE__, s_con[i].reg->name);
			rv = EXIT_FAILURE;
			goto ERR_FILE_NAME_FREE;
		}

		// Open files.
		tmp = access(file_name, F_OK);  // Check if file exists (for CSV header).
		s_con[i].csv_file = open(file_name, O_CREAT | O_WRONLY | O_APPEND, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
		if (0 > s_con[i].csv_file) {
			fprintf(stderr, "%s:%i: opening file %s failed: %s\n", __FILE__, __LINE__, file_name, strerror(errno));
			rv = EXIT_FAILURE;
			goto ERR_FILE_NAME_FREE;
		}
		// If file did not exist, write header.
		if (tmp) {
			errno = 0;
			written = write(s_con[i].csv_file, s_con[i].reg->csv_header, strlen(s_con[i].reg->csv_header));
			if (0 > written) {
				fprintf(stderr, "%s:%d: writing CSV header failed: %s\n", __FILE__, __LINE__, strerror(errno));
				rv = EXIT_FAILURE;
				goto ERR_FILE_NAME_FREE;
			}
		}
	}

	// FLOW #9.1
	// Create tasks
	tasks_amt = s_con_amt + TASK_TYPE_AMT - 1;
	tasks = malloc(tasks_amt * sizeof (struct task));
	if (NULL == tasks) {
		fprintf(stderr, "%s:%i: malloc of %zu bytes failed\n", __FILE__, __LINE__, file_name_length * sizeof (char));
		rv = EXIT_FAILURE;
		goto ERR_FILE_NAME_FREE;
	}
	// FLOW #9.2
	// Init tasks
	// Sensors
	for (size_t i = 0; s_con_amt > i; i++) {
		tasks[i].type = SENSOR;
		tasks[i].state = RUNNING;
		tasks[i].next_time = (struct timespec) {0, 0};
		tasks[i].p = &(s_con[i]);
		sensor = (i2c_sensor *) (tasks[i].p);
		tasks[i].active_phases = sensor->reg->active_phases;
		s_con[i].t = &(tasks[i]);

	}
	// LED tasks
	tasks[s_con_amt].type = LED_MASTER;
	tasks[s_con_amt].state = RUNNING;
	tasks[s_con_amt].active_phases = START | ASCEND;
	errno = 0;
	if (0 > clock_gettime(CLOCK_MONOTONIC, &(tasks[s_con_amt].next_time))) {
		fprintf(stderr, "%s:%i: acquiring time failed: %s\n", __FILE__, __LINE__, strerror(errno));
		rv = EXIT_FAILURE;
		goto ERR_TASKS_FREE;
	}
	tasks[s_con_amt + 1].type = LED_SENSORS;
	tasks[s_con_amt + 1].state = RUNNING;
	tasks[s_con_amt + 1].active_phases = START | ASCEND;
	errno = 0;
	if (0 > clock_gettime(CLOCK_MONOTONIC, &(tasks[s_con_amt + 1].next_time))) {
		fprintf(stderr, "%s:%i: acquiring time failed: %s\n", __FILE__, __LINE__, strerror(errno));
		rv = EXIT_FAILURE;
		goto ERR_TASKS_FREE;
	}
	// Fallback task for state change, if sensors or state transitions fail
	tasks[s_con_amt + 2].type = FALLBACK_STATE_TO_RECOVERY_CHANGER;
	tasks[s_con_amt + 2].state = RUNNING;
	tasks[s_con_amt + 2].active_phases = START | FLIGHT | RECOVERY;
	tasks[s_con_amt + 2].next_time = (struct timespec) {0, 0};
	tasks[s_con_amt + 2].p = NULL;
	// Camera USB task
	tasks[s_con_amt + 3].type = CAM_USB_TASK;
	tasks[s_con_amt + 3].state = RUNNING;
	tasks[s_con_amt + 3].active_phases = START | FLIGHT | RECOVERY;
	tasks[s_con_amt + 3].next_time = (struct timespec) {0, 0};
	// Camera raspi task
	tasks[s_con_amt + 4].type = CAM_RASPI_TASK;
	tasks[s_con_amt + 4].state = RUNNING;
	tasks[s_con_amt + 4].active_phases = START | FLIGHT | RECOVERY;
	tasks[s_con_amt + 4].next_time = (struct timespec) {0, 0};
	// GSM sender task
	tasks[s_con_amt + 5].type = GSM_TASK;
	tasks[s_con_amt + 5].state = RUNNING;
	tasks[s_con_amt + 5].active_phases = START | FLIGHT | RECOVERY;
	errno = 0;
	if (0 > clock_gettime(CLOCK_MONOTONIC, &(tasks[s_con_amt + 5].next_time))) {
		fprintf(stderr, "%s:%i: acquiring time failed: %s\n", __FILE__, __LINE__, strerror(errno));
		rv = EXIT_FAILURE;
		goto ERR_TASKS_FREE;
	}
	// Watchdog task
	tasks[s_con_amt + 6].type = WATCHDOG;
	tasks[s_con_amt + 6].state = RUNNING;
	tasks[s_con_amt + 6].active_phases = START | FLIGHT | RECOVERY;
	errno = 0;
	if (0 > clock_gettime(CLOCK_MONOTONIC, &(tasks[s_con_amt + 6].next_time))) {
		fprintf(stderr, "%s:%i: acquiring time failed: %s\n", __FILE__, __LINE__, strerror(errno));
		rv = EXIT_FAILURE;
		goto ERR_TASKS_FREE;
	}
	// Watchpuppy task
	tasks[s_con_amt + 7].type = WATCHPUPPY;
	tasks[s_con_amt + 7].state = RUNNING;
	tasks[s_con_amt + 7].active_phases = START | FLIGHT | RECOVERY;
	errno = 0;
	if (0 > clock_gettime(CLOCK_MONOTONIC, &(tasks[s_con_amt + 7].next_time))) {
		fprintf(stderr, "%s:%i: acquiring time failed: %s\n", __FILE__, __LINE__, strerror(errno));
		rv = EXIT_FAILURE;
		goto ERR_TASKS_FREE;
	}

	// FLOW #10
	// Update states by phases
	if (EXIT_FAILURE == task_update_states_by_phase(tasks, tasks_amt, active_phase)) {
		fprintf(stderr, "%s:%i: task_update_states_by_phase failed\n", __FILE__, __LINE__);
		rv = EXIT_FAILURE;
		goto ERR_TASKS_FREE;
	}

	// Main loop
	while (QUIT != active_phase && EXIT_SUCCESS == task_get_next(tasks, tasks_amt, &task)) {

		// Is task ready? If not, sleep remaining time
		if (0 > clock_gettime(CLOCK_MONOTONIC, &cur_time)) {
			fprintf(stderr, "%s:%i: acquiring time failed: %s\n", __FILE__, __LINE__, strerror(errno));
			rv = EXIT_FAILURE;
			goto ERR_TASKS_FREE;
		}
		if (timespec_gt(task->next_time, cur_time)) {
			diff_time = timespec_sub(task->next_time, cur_time);
			do {
				errno = 0;
			} while (0 > clock_nanosleep(CLOCK_MONOTONIC, 0,&diff_time, &diff_time) && EINTR == errno);
		}

		switch (task->type) {
			case SENSOR:

			sensor = (i2c_sensor*) (task->p);
				// Sensor measurement cycle
				// Repeat, as long as sensor isn't sleeping and there's no delay
				do {

					// TODO-DEL - Signal-Blocking while sensors running not useful in case of program freezing
//					// Block all signals.
//					tmp = sigprocmask(SIG_BLOCK, &all, &normal);
//					if (-1 == tmp) {
//						fprintf(stderr, "%s:%i: setting blocking signal mask failed\n", __FILE__, __LINE__);
//					}

					switch (sensor->state) {

						case INIT:
							if (EXIT_FAILURE == sensor->reg->init(active_phase, &(sensor->it_rem), &(sensor->processing_time))) { // Init_Err
								fprintf(stderr, "%s:%i: init for %s failed\n", __FILE__, __LINE__, sensor->reg->name);
								sensor->err_seq++;
								sensor->err_total++;
								if (ERR_THRESHOLD <= sensor->err_seq) {
									sensor->state = FAILURE;
									sensor->failure_total++;
									if (sensor == sensor_height) {
										sensor_get_best_by_date_type(s_con, s_con_amt, HEIGHT, &sensor_height);
									}
									sensor->processing_time = ERR_PERIOD_MS;
								} else {
									sensor->processing_time = ERR_SEQ_DELAY_MS;
								}
							} else { // Init_Succ
								sensor->err_seq = 0;
								if (0 < sensor->processing_time) {
									// Update next_time
									if (0 > clock_gettime(CLOCK_MONOTONIC, &cur_time)) {
										fprintf(stderr, "%s:%i: acquiring time failed: %s\n", __FILE__, __LINE__, strerror(errno));
										rv = EXIT_FAILURE;
										goto ERR_TASKS_FREE;
									}
									task->next_time = timespec_add_ms(cur_time, sensor->processing_time);
								}
								if (0 < sensor->it_rem) { // Init_Unfinished
									sensor->it_rem--;
								} else { // Init_Finished
									sensor->state = MEASURE;
									sensor->it_rem = -1;
									// Set sensor->next_cycle_time
									if (0 > clock_gettime(CLOCK_MONOTONIC, &(sensor->next_cycle_time))) {
										fprintf(stderr, "%s:%i: acquiring time failed: %s\n", __FILE__, __LINE__, strerror(errno));
										rv = EXIT_FAILURE;
										goto ERR_TASKS_FREE;
									}
									sensor->next_cycle_time = timespec_add_ms(sensor->next_cycle_time, sensor->reg->cycle_len * CYCLE_LEN_MULT);
								}
							}
							break;
						case MEASURE:
							if (EXIT_FAILURE == sensor->reg->measure(active_phase, &(sensor->measurements), &(sensor->it_rem), &(sensor->processing_time))) { // Measure_Err
								fprintf(stderr, "%s:%i: measure for %s failed\n", __FILE__, __LINE__, sensor->reg->name);
								sensor->err_seq++;
								sensor->err_total++;
								if (ERR_THRESHOLD <= sensor->err_seq) {
									sensor->state = FAILURE;
									sensor->failure_total++;
									if (sensor == sensor_height) {
										sensor_get_best_by_date_type(s_con, s_con_amt, HEIGHT, &sensor_height);
									}
									sensor->processing_time = 0;
								} else {
									sensor->processing_time = ERR_SEQ_DELAY_MS;
								}
							} else { // Measure_Succ
								sensor->err_seq = 0;
								if (0 < sensor->it_rem) { // Measure_Unfinished
									sensor->it_rem--;
									if (0 < sensor->processing_time) {
										// Update next_time
										if (0 > clock_gettime(CLOCK_MONOTONIC, &cur_time)) {
											fprintf(stderr, "%s:%i: acquiring time failed: %s\n", __FILE__, __LINE__, strerror(errno));
											rv = EXIT_FAILURE;
											goto ERR_TASKS_FREE;
										}
										task->next_time = timespec_add_ms(cur_time, sensor->processing_time);
									}
								} else { // Measure_Finished
									sensor->it_rem = -1;
									// TODO-UC
//									task->next_time = timespec_add_ms(sensor->next_cycle_time, sensor->processing_time);
//									sensor->next_cycle_time = timespec_add_ms(sensor->next_cycle_time, sensor->reg->cycle_len * CYCLE_LEN_MULT);

									// TODO-TEST-2-B
									if (0 > clock_gettime(CLOCK_MONOTONIC, &cur_time)) {
										fprintf(stderr, "%s:%i: acquiring time failed: %s\n", __FILE__, __LINE__, strerror(errno));
										rv = EXIT_FAILURE;
										goto ERR_TASKS_FREE;
									}
									task->next_time = timespec_add_ms(cur_time, sensor->processing_time);
									sensor->next_cycle_time = timespec_add_ms(sensor->next_cycle_time, sensor->reg->cycle_len * CYCLE_LEN_MULT);
									if (timespec_gt(sensor->next_cycle_time, task->next_time)) {
										task->next_time = sensor->next_cycle_time;
									} else {
										fprintf(stderr, "%s:%i: %ld.%03ld: sensor %s cycle_len exceeded\n", __FILE__, __LINE__, cur_time.tv_sec, cur_time.tv_nsec / 1000000, sensor->reg->name);
										sensor->next_cycle_time = task->next_time;
									}
									// TODO-TEST-2-E

									sensor->processing_time = 1; // Actual value doesn't matter, just to stop sensor loop
									if (EXIT_FAILURE == measurement_append_to_csv_file(sensor->csv_file, sensor->measurements)) {
										fprintf(stderr, "%s:%i: append_to_csv_file for %s's data failed\n", __FILE__, __LINE__, sensor->reg->name);
									}
									// Test for phase switching conditions.
									if (sensor_height == sensor) { // New height value?
										// TODO-MV - move to sensor_height probing an save in pointer
										for (size_t i = 0; sensor->reg->date_amt > i; i++) { // get height
											if (HEIGHT == sensor->reg->types[i]) {
												height_cur = sensor->measurements.data[i];
												break;
											}
										}
										if (HEIGHT_MIN < height_cur || HEIGHT_MAX > height_cur) { // No false value?
											dheight_sum += height_cur - height_last;
											height_last = height_cur;
										}
										height_cnt++;
										if (HEIGHT_AMT == height_cnt) { // Enough values collected? Calculate vert. velocity
											v_velo = dheight_sum / height_cnt / (double) sensor->reg->cycle_len * 1000;
											height_cnt = 0.0;
											dheight_sum = 0.0;

											if (v_velo > V_VELO_HALT_TO_FLIGHT_THRESHOLD || v_velo < (-1 * V_VELO_HALT_TO_FLIGHT_THRESHOLD)) { // Threshold to detect movement exceeded?
												v_movement_seq = v_movement_seq << 1 | 0x01;
												if (START == active_phase && V_MOVEMENT_FLIGHING_MSK == (v_movement_seq & V_MOVEMENT_FILTER_MSK)) { // In START pahse and enough movements detected?
													printf("Phase switched to FLIGHT!\n");
													active_phase = FLIGHT;
													tmp = write_phase(active_phase);
													if (EXIT_FAILURE == tmp) {
														fprintf(stderr, "%s:%i: writing updated phase failed: active_phase=%u\n", __FILE__, __LINE__, active_phase);
													}
													// Update states by phases
													if (EXIT_FAILURE == task_update_states_by_phase(tasks, tasks_amt, active_phase)) {
														fprintf(stderr, "%s:%i: task_update_states_by_phase failed\n", __FILE__, __LINE__);
														rv = EXIT_FAILURE;
														goto ERR_TASKS_FREE;
													}
												}
											} else { // Thresehold not exceeded
												v_movement_seq = (v_movement_seq << 1) | 0x0000;
												if (FLIGHT == active_phase && V_MOVEMENT_LANDED_MSK == (v_movement_seq & V_MOVEMENT_FILTER_MSK)) { // Enough stationaries detected?
													printf("Phase switched to RECOVERY!\n");
													active_phase = RECOVERY;
													tmp = write_phase(active_phase);
													if (EXIT_FAILURE == tmp) {
														fprintf(stderr, "%s:%i: writing updated phase failed: active_phase=%u\n", __FILE__, __LINE__, active_phase);
													}
													// Update states by phases
													if (EXIT_FAILURE == task_update_states_by_phase(tasks, tasks_amt, active_phase)) {
														fprintf(stderr, "%s:%i: task_update_states_by_phase failed\n", __FILE__, __LINE__);
														rv = EXIT_FAILURE;
														goto ERR_TASKS_FREE;
													}
												}
											}
//											printf("s_name = %s\nheight_cur = %lf\nv_velo = %lf\nv_movement_seq = 0x%04x\n\n", sensor->reg->name, height_cur, v_velo, v_movement_seq); // TODO-DEL
										}
									}
								}
							}
							break;
						case FAILURE:
							if (EXIT_FAILURE == sensor->reg->is_connected()) { // Sensor still dead
								fprintf(stderr, "%s:%i: reconnect for %s failed\n", __FILE__, __LINE__, sensor->reg->name);
								sensor->err_total++;
								sensor->processing_time = ERR_PERIOD_MS;
								task->next_time = timespec_add_ms(task->next_time, sensor->processing_time);
							} else { // Sensor recovered
								fprintf(stderr, "%s:%i: reconnect for %s successful\n", __FILE__, __LINE__, sensor->reg->name);
								sensor->err_seq = 0;
								sensor->state = INIT;
								sensor->it_rem = -1;
								if (EXIT_SUCCESS == sensor_has_date_type(*sensor, HEIGHT)) {
									sensor_get_best_by_date_type(s_con, s_con_amt, HEIGHT, &sensor_height);
								}
								sensor->processing_time = 0;
							}
							break;
						default:
							fprintf(stderr, "%s:%i: sensor \"%s\" has illegal state: %d. Stopped sensor task\n", __FILE__, __LINE__, sensor->reg->name, sensor->state);
							task->state = STOPPED;
					}
					// TODO-DEL - Signal-Blocking while sensors running not useful in case of program freezing
//					// Reset signal mask.
//					tmp = sigprocmask(SIG_SETMASK, &normal, NULL);
//					if (-1 == tmp) {
//						fprintf(stderr, "%s:%i: returning to normal signal mask failed\n", __FILE__, __LINE__);
//					}
				} while (SLEEP != sensor->state && 0 >= sensor->processing_time);
				break;

			case LED_MASTER:
				if (START == active_phase) {
					// Blinks if master is running.
					master_LED_on = !master_LED_on;
					set_taskLED(task->type, master_LED_on);

					task->next_time = timespec_add_ms(task->next_time, MASTER_LED_MS);
				} else {
					set_taskLED(task->type, 0);
					task->state = STOPPED;
				}
				break;

			case LED_SENSORS:
				if (START == active_phase) {
					// Count current failures.
					tmp = 0;
					for (size_t i = 0; i < s_con_amt; ++i) {
						if (FAILURE == s_con[i].state) {
							++tmp;
						}
					}

					// Disconnected sensors or current failures -> off.
					if (SENSOR_AMT != s_con_amt || tmp) {
						sensors_LED_on = 0;
						// Any past failures -> blinking; no failures at all -> on.
					} else {
						tmp = 0;
						for (size_t i = 0; i < s_con_amt; ++i) {
							tmp += s_con[i].failure_total;
						}
						sensors_LED_on = tmp ? !sensors_LED_on : 1;
					}
					set_taskLED(task->type, sensors_LED_on);

					task->next_time = timespec_add_ms(task->next_time, SENSORS_LED_MS);
				} else {
					set_taskLED(task->type, 0);
					task->state = STOPPED;
				}
				break;

			case CAM_RASPI_TASK:
				switch (active_phase) {
					case RECOVERY:
						if (raspicam_is_active) {
							CAMERAS[0].stop();
							raspicam_is_active = 0;
						}
						break;
					default: // START, FLIGHT, etc
						if (!raspicam_is_active) {
							CAMERAS[0].start(RASPICAM_MODE, RASPICAM_FILENAME, RASPICAM_PARM);
							raspicam_is_active = 1;
						}
				}
				task->state = STOPPED;
				break;

			case CAM_USB_TASK:
				switch (active_phase) {
					case RECOVERY:
						if (raspicam_is_active) {
							CAMERAS[1].stop();
							usb_camera_is_active = 0;
						}
						break;
					default: // START, FLIGHT, etc
						if (!usb_camera_is_active) {
							CAMERAS[1].start(USB_CAMERA_MODE, USB_CAMERA_FILENAME, USB_CAMERA_PARM);
							usb_camera_is_active = 1;
						}
				}
				task->state = STOPPED;
				break;

			case GSM_TASK:
//				send_GSM(&sensor_gps->measurements); // TODO-UC
				task->next_time = timespec_add_ms(task->next_time, GSM_SEND_MS);
				break;

			case FALLBACK_STATE_TO_RECOVERY_CHANGER:
				if (EXIT_FAILURE == fallback_recovery_task(task, &active_phase, cur_time)) {
					fprintf(stderr, "%s:%s:%i: fallback_recovery_task failed\n", __FILE__, __func__, __LINE__);
					rv = EXIT_FAILURE;
					goto ERR_TASKS_FREE;
				}
				break;

			case WATCHDOG:
				wake_watchdog();
				task->next_time = timespec_add_ms(task->next_time, WATCHDOG_KEEPALIVE_MS);
				break;

			case WATCHPUPPY:
				system(WATCHPUPPY_SIGNALING_CMD);
				task->next_time = timespec_add_ms(task->next_time, WATCHPUPPY_SIGNALING_CYCLE_S * 1000);
				break;

			default:
				fprintf(stderr, "%s:%i: task with faulty task_type\n", __FILE__, __LINE__);
		}
	}

	// Cleanup in reverse order.
ERR_TASKS_FREE:
	free(tasks);
ERR_FILE_NAME_FREE:
	free(file_name);
ERR_S_CON_DESTROY:
	for (size_t i = 0; s_con_amt > i; i++) {
		measurement_destroy(&(s_con[i].measurements));
	}
	free(s_con);
ERR_BCM_I2C_END:
	bcm2835_i2c_end();
ERR_BCM_LIB_CLOSE:
	if (!bcm2835_close()) {
		rv = EXIT_FAILURE;
	}
ERR_SIG_UPDT:
	tmp = sigaction(UPDATE_SIGNAL, &update_action_default, NULL);
	if (0 > tmp) {
		fprintf(stderr, "%s:%d: resetting phase update handler to default failed\n", __FILE__, __LINE__);
		rv = EXIT_FAILURE;
	}
ERR_SIG_MAINT:
	tmp = sigaction(MAINTENANCE_SIGNAL, &maintenance_action_default, NULL);
	if (0 > tmp) {
		fprintf(stderr, "%s:%d: resetting maintenance handler to default failed\n", __FILE__, __LINE__);
		rv = EXIT_FAILURE;
	}
ERR:
	return rv;
}


int read_phase(phase *const p) {
	FILE *file = NULL;
	int tmp = 0;
	int rv = EXIT_SUCCESS;

	if (NULL == p) {
		fprintf(stderr, "%s:%i: no phase given\n", __FILE__, __LINE__);
		return EXIT_FAILURE;
	}

	/* Automated creation not specified in header.
	// File doesn't exist.
	tmp = access(file_name, F_OK);
	if (tmp) {
	 *p = START;
	 return write_phase(*p);
	 }
	 */

	file = fopen(PERSISTENCE_PATH, "r");
	if (NULL == file) {
		fprintf(stderr, "%s:%i: opening persistence file for reading failed: %s\n", __FILE__, __LINE__, strerror(errno));
		return EXIT_FAILURE;
	}

	errno = 0;
	tmp = fscanf(file, "%u", p);
	if (1 != tmp) {
		fprintf(stderr, "%s:%i: scanning for phase failed: %s\n", __FILE__, __LINE__, 0 == errno ? "no value" : strerror(errno));
		rv = EXIT_FAILURE;
	}

	tmp = fclose(file);
	if (tmp) {
		fprintf(stderr, "%s:%i: closing persistence file after reading failed: %s\n", __FILE__, __LINE__, strerror(errno));
		return EXIT_FAILURE;
	}

	return rv;
}


int write_phase(phase p) {
	FILE *file = NULL;
	int tmp = 0;

	file = fopen(PERSISTENCE_PATH, "w");
	if (NULL == file) {
		fprintf(stderr, "%s:%i: opening persistence file for writing failed: %s\n", __FILE__, __LINE__, strerror(errno));
		return EXIT_FAILURE;
	}

	tmp = fprintf(file, "%i", p);
	if (0 >= tmp) {
		fprintf(stderr, "%s:%i: printing phase failed\n", __FILE__, __LINE__);
		return EXIT_FAILURE;
	}

	tmp = fclose(file);
	if (tmp) {
		fprintf(stderr, "%s:%i: closing persistence file after writing failed: %s\n", __FILE__, __LINE__, strerror(errno));
		return EXIT_FAILURE;
	}

	return EXIT_SUCCESS;
}



void sh_maintenance(int sig) {
	unsigned long total_failures = 0;
	size_t in_failure = 0;

	size_t i_all = 0;

	if (MAINTENANCE_SIGNAL == sig) {
		// Collect combined metrics.
		for (size_t i = 0; i < s_con_amt; ++i) {
			total_failures += s_con[i].failure_total;
			if (FAILURE == s_con[i].state) {
				++in_failure;
			}
		}

		// TODO-IMPL - Show master status as following:
		// Master-Status CRITICAL, all height sensors in FAILURE, or no height sensor
		printf("\n[%s][%s] %-10s (", 0 == total_failures ? "\033[0;92m GOOD \033[0m" : (0 == in_failure ? "\033[0;93m OKAY \033[0m" : "\033[0;33m ALRM \033[0m"), 0 < s_con_amt ? "\033[0;36mACTIVE\033[0m" : "\033[0;30m DEAD \033[0m", "master");
		switch (active_phase) {
			// Single phases.
			case START:
				printf("START");
				break;
			case ASCEND:
				printf("ASCEND");
				break;
			case DESCEND:
				printf("DESCEND");
				break;
			case RECOVERY:
				printf("RECOVERY");
				break;
				// Combined phases (name strings ending with '*').
			case FLIGHT:
				printf("FLIGHT*");
				break;
			default:
				printf("\033[0;90m?active_phase=%u?\033[0m", active_phase);
		}
		printf(" phase, %zu/%zu sensors connected, %lu failures total, %zu sensors in FAILURE)\n\n", s_con_amt, SENSOR_AMT, total_failures, in_failure);

		printf("[ COND ][  TASK   ][STATE ]    NAME    (ERROR STATISTICS)\n");
		for (size_t i = 0; i < s_con_amt; ++i) {
			printf("[%s][%s][%s] %-10s (%lu failures total, %lu errors total, %u erorrs in sequence)\n",
					0 == s_con[i].err_total ? "\033[0;92m GOOD \033[0m" : (FAILURE == s_con[i].state ? "\033[0;91m FAIL \033[0m" : (0 == s_con[i].err_seq ? "\033[0;33m OKAY \033[0m" : "\033[0;33mINTRUP\033[0m")),
					RUNNING == s_con[i].t->state ? "\033[0;92m RUNNING \033[0m" : "\033[0;34m STOPPED \033[0m",
					INIT == s_con[i].state ? "\033[0;95m INIT \033[0m" : (!s_con[i].reg->active_phases ? "\033[0;35mHYBERN\033[0m" : (s_con[i].reg->active_phases & active_phase ? "\033[0;36mACTIVE\033[0m" : "\033[0;34mASLEEP\033[0m")),
					s_con[i].reg->name, s_con[i].failure_total, s_con[i].err_total, s_con[i].err_seq);
		}
		if (SENSOR_AMT > s_con_amt) {
			for (size_t i_con = 0; SENSOR_AMT > i_all && i_con < s_con_amt; ++i_all) {
				if (&SENSORS[i_all] == s_con[i_con].reg) {
					++i_con;
				} else {
					printf("[\033[0;30mDISCON\033[0m][      ] %-10s\n", SENSORS[i_all].name);
				}
			}
			for (; SENSOR_AMT > i_all; ++i_all) {
				printf("[\033[0;30mDISCON\033[0m][      ] %-10s\n", SENSORS[i_all].name);
			}
		}

		fflush(stdout);
	}
}

void sh_update_phase(int sig) {
	sigset_t all;
	sigset_t normal;
	int tmp = 0;

	tmp = sigfillset(&all);
	if (-1 == tmp) {
		fprintf(stderr, "%s:%i: filling blocking signal mask failed\n", __FILE__, __LINE__);
	}
	tmp = sigprocmask(SIG_BLOCK, &all, &normal);
	if (-1 == tmp) {
		fprintf(stderr, "%s:%i: setting blocking signal mask failed\n", __FILE__, __LINE__);
	}

	if (UPDATE_SIGNAL == sig) {
		tmp = read_phase(&active_phase);
		if (EXIT_FAILURE == tmp) {
			fprintf(stderr, "%s:%i: reading phase on signal failed\n", __FILE__, __LINE__);
		}
		tmp = task_update_states_by_phase(tasks, tasks_amt, active_phase);
		if (EXIT_FAILURE == tmp) {
			fprintf(stderr, "%s:%s:%i: task_update_states_by_phase failed\n", __FILE__, __func__, __LINE__);
		}
	}

	tmp = sigprocmask(SIG_SETMASK, &normal, NULL);
	if (-1 == tmp) {
		fprintf(stderr, "%s:%i: returning to normal signal mask failed\n", __FILE__, __LINE__);
	}
}


int fallback_recovery_task(task *thiz, phase *cur_phase, struct timespec cur_time) {
	FILE *file = NULL;
	int rv = EXIT_SUCCESS;
	int rv_f = 0;

	if (NULL == thiz) {
		fprintf(stderr, "%s:%s:%i: Input thiz is NULL\n", __FILE__, __func__, __LINE__);
		return EXIT_FAILURE;
	}
	if (NULL == cur_phase) {
		fprintf(stderr, "%s:%s:%i: Input cur_phase is NULL\n", __FILE__, __func__, __LINE__);
		return EXIT_FAILURE;
	}

	switch (*cur_phase) {
		case START: // Set next_time and save it
			// Set next_time
			thiz->next_time.tv_sec = cur_time.tv_sec + MAX_FLIGHT_TIME_S;
			thiz->next_time.tv_nsec = cur_time.tv_nsec;
			// Write next_time to file
			file = fopen(MAX_FLIGHT_TIME_PATH, "w");
			if (NULL == file) {
				fprintf(stderr, "%s:%s:%i: fopen failed: %s\n", __FILE__, __func__, __LINE__, strerror(errno));
				return EXIT_FAILURE;
			}
			rv_f = fprintf(file, "%ld\n%ld", thiz->next_time.tv_sec, thiz->next_time.tv_nsec);
			if (0 >= rv_f) {
				fprintf(stderr, "%s:%s:%i: fprintf failed and returned %d\n", __FILE__, __func__, __LINE__, rv_f);
				rv = EXIT_FAILURE;
			}
			rv_f = fclose(file);
			if (EOF == rv_f) {
				fprintf(stderr, "%s:%s:%i: fclose failed: %s\n", __FILE__, __func__, __LINE__, strerror(errno));
				return EXIT_FAILURE;
			}
			break;

		case RECOVERY: // Stopp own task
			thiz->state = STOPPED;
		break;

		default: // Check for passed MAX_FLIGHT_TIME
			// Is next_time not set?
			if (0 == thiz->next_time.tv_sec && 0 == thiz->next_time.tv_nsec) {
				// Read next_time from file
				file = fopen(MAX_FLIGHT_TIME_PATH, "r");
				if (NULL == file) {
					fprintf(stderr, "%s:%s:%i: fopen failed: %s\n", __FILE__, __func__, __LINE__, strerror(errno));
					return EXIT_FAILURE;
				}
				rv_f = fscanf(file, "%ld\n%ld", &(thiz->next_time.tv_sec), &(thiz->next_time.tv_nsec));
				errno = 0;
				if (2 != rv_f) {
					fprintf(stderr, "%s:%i: fscanf failed: %s\n", __FILE__, __LINE__, 0 == errno ? "faulty file content" : strerror(errno));
					rv = EXIT_FAILURE;
				}
				rv_f = fclose(file);
				if (EOF == rv_f) {
					fprintf(stderr, "%s:%s:%i: fclose failed: %s\n", __FILE__, __func__, __LINE__, strerror(errno));
					return EXIT_FAILURE;
				}
			}
			// Is MAX_FLIGHT_TIME reached?
			if (timespec_lt(thiz->next_time, cur_time)) {
				fprintf(stderr, "%s:%s:%i: MAX_FLIGHT_TIME_S = %ld s reached. Switched state to RECOVERY.\n", __FILE__, __func__, __LINE__, MAX_FLIGHT_TIME_S);
				*cur_phase = RECOVERY;
				// Update phase
				rv_f = task_update_states_by_phase(tasks, tasks_amt, active_phase);
				if (EXIT_FAILURE == rv_f) {
					fprintf(stderr, "%s:%s:%i: task_update_states_by_phase failed\n", __FILE__, __func__, __LINE__);
					return EXIT_FAILURE;
				}
				rv_f = write_phase(*cur_phase);
				if (EXIT_FAILURE == rv_f) {
					fprintf(stderr, "%s:%s:%i: write_phase failed\n", __FILE__, __func__, __LINE__);
					return EXIT_FAILURE;
				}
				// Stopp own task
				thiz->state = STOPPED;
			}
	}
	
	return rv;
}
definitions.h
/**
 * @file  definitions.h
 * @brief Definitions used by multiple header files
 *
 * @author Florian Schwarz
 */

#ifndef INCLUDE_DEFINITIONS_H_
#define INCLUDE_DEFINITIONS_H_


/**
 * @brief Possible operating phases of weather balloon as bit mask in chronological
 *        order.
 *
 * Combine masks with bitwise OR ("|") and check for phase with bitwise AND
 * ("&"). The result of this is automatically usable as boolean value.\n
 * \n
 * Keeping a chronological order is important for being able to switch through
 * the phases by just bit-shifting the active phase.
 */
typedef enum phase {
	START    = 0x01,  ///< Pre-flight phase.
	ASCEND   = 0x02,  ///< Ascending phase.
	DESCEND  = 0x04,  ///< Descending phase.
	RECOVERY = 0x08,  ///< Recovery phase.

	// Special phases.
	QUIT     = 0x10,   ///< Not an actual phase. Triggers program termination.

	// Combined phases.
	FLIGHT   = 0x06    ///< ASCEND | DESCEND
} phase;

#endif  // INCLUDE_DEFINITIONS_H_
measurement.h
/**
 * @file  measurement.h
 * @brief Definitions for the Master programm.
 *
 * @author Florian Schwarz, Jonas Gaida
 */

#ifndef INCLUDE_MEASUREMENTDATA_H_
#define INCLUDE_MEASUREMENTDATA_H_

#include <stddef.h>  // size_t
#include <time.h>  // struct timespec



/**
 * @brief Possible types of data stored in a measurement struct data field.
 *
 * @sa measurement
 */
typedef enum date_type {
	EXOTIC,             ///< Something else.
	HEIGHT,             ///< Height in m (meter).
	PRESSURE,           ///< Atmospheric pressure in hPa (hectopascal).
	TEMPERATURE,        ///< Temperature in °C (degree Celsius).
	HUMIDITY,           ///< Relative humidity in % (percent)