Kristian MadunicDaniel StümkeHodyra DanielMichael KoidisA Team
Published

I.S.I - Intelligente Schwerkraft Infusion

I.S.I is an intelligent gravity infusion stand that provides information about the state of its infusion.

IntermediateWork in progressOver 8 days708
I.S.I - Intelligente Schwerkraft Infusion

Things used in this project

Story

Read more

Code

I.S.I Webapplication

HTML
<html><head>
	<title>I.S.I. Overview</title>
	<style>
	* {
		margin: 0;
		padding: 0;
	}
	body {
		background-color: #F2EEE9;
		font: normal 13px/1.5 Arial, Serif;
		color: #333;
	}
	.wrapper {
		width: 705px;
		margin: 20px auto;
		padding: 20px;
	}
	h1 {
		color: #59c4f2;
		font-size: 20px;
		font-weight: normal;
		text-transform: uppercase;
		padding: 8px 0px;
	}
	.clear {
		clear: both;
	}
	.items {
		display: block;
	}
	.item {
		background-color: #fff;
		float: left;
		margin: 0 10px 10px 0;
		width: 205px;
		padding: 10px;
	}
	.item img {
		display: block;
		margin: auto;
		width: 170px;
		padding: 15px;
	}
	h2 {
		font-size: 13px;
		display: block;
		border-bottom: 1px solid #ccc;
		margin: 0 0 10px 0;
		padding: 0 0 5px 0;
	}
	label,
	button {
		border: 1px solid #722A1B;
		padding: 4px 14px;
		background-color: #fff;
		color: #722A1B;
		text-transform: uppercase;
		margin: 5px 0;
		font-weight: bold;
		cursor: pointer;
	}
	label {
		float: right;
	}
	img#logo {
    width: 100px;
	}

	.loader {
	  border: 16px solid #f3f3f3;
	  border-radius: 50%;
	  border-top: 16px solid #3498db;
	  width: 120px;
	  height: 120px;
    margin: auto;
	  -webkit-animation: spin 2s linear infinite; /* Safari */
	  animation: spin 2s linear infinite;
	}

		/* Safari */
		@-webkit-keyframes spin {
		  0% { -webkit-transform: rotate(0deg); }
		  100% { -webkit-transform: rotate(360deg); }
		}

		@keyframes spin {
		  0% { transform: rotate(0deg); }
		  100% { transform: rotate(360deg); }
		}

		.trigger, .disabled_trigger{
			text-transform: uppercase;
			margin-top: 10%;
			display: block;
		}

		.disabled_trigger {
			opacity: 0.5;
			pointer-events: none;
		}

		.popup__textbox{
			color: #b43d54;
			line-height: 25px;
			font-size: 1.1em;
			letter-spacing: 1px;
		}

	/* Start popup css */

	@keyframes bg {
	    from {opacity: 0;}
	    to {opacity: 1;}
	}

	@keyframes inner {
	    0% {transform: scale(0.8);}
	    50% {transform: scale(1.06);}
		100% {transform: scale(1);}
	}

	@-webkit-keyframes bg {
	    from {opacity: 0;}
	    to {opacity: 1;}
	}

	@-webkit-keyframes inner {
	    0% {transform: scale(0.8);}
	    50% {transform: scale(1.06);}
		100% {transform: scale(1);}
	}

	.popup__check{
		display: none;
	}

	.popup__base, .popup__bg{
		position: fixed;
		top: 0;
		right: 0;
		bottom: 0;
		left: 0;
		opacity: 0;
		cursor:zoom-out;
	}

	.popup__base{
		background-color: rgba(0,0,0,0.5);
		display: none;
	}

	.popup__check:checked + .popup__base{
		display: block;
		animation-name: bg;
	    animation-duration: .5s;
		animation-fill-mode:forwards;
		-webkit-animation-name: bg;
	    -webkit-animation-duration: .5s;
		-webkit-animation-fill-mode:forwards;
	}

	.popup__inner{
		position: absolute;
		z-index: 10;
		width: 70%;
		height: 70%;
		background-color: #fff;
		top: 15%;
		left: 15%;
		display: block;
		cursor:default;
	}

	.popup__check:checked + .popup__base .popup__inner{
		animation-name: inner;
	    animation-duration: .5s;
		animation-fill-mode:forwards;
		-webkit-animation-name: inner;
	    -webkit-animation-duration: .5s;
		-webkit-animation-fill-mode:forwards;
	}

	.popup__textbox{
		height: 95%;
		width: 95%;
		padding-left: 2.5%;
		padding-right: 2.5%;
		margin-top: 10px;
		overflow: auto;
	}

	.popup__calign{
		float: right;
		padding-right: 60px;
		font-size: 50px;
	}

	.popup__close{
		-webkit-transform: rotate(45deg);
		-moz-transform: rotate(45deg);
		-ms-transform: rotate(45deg);
		-o-transform: rotate(45deg);
		display: block;
		position: absolute;
		z-index: 10;
		text-align: right;
		cursor: pointer;
		color: #b43d54;
    border: none;
    padding: 0;
	}
	</style>
</head>
<body>
	<!-- wrapper -->
	<div class="wrapper">

		<img id="logo" src="logo.png"><h1>System Overview:</h1>



		<div class="clear"></div>
		<!-- items -->
		<div class="items" id="item-container">
		</div>
			<!--/ items -->
		</div>
		<!--/ wrapper -->

		<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.js"></script>
		<script src="https://cdnjs.cloudflare.com/ajax/libs/bluebird/3.5.3/bluebird.js"></script>
		<script>
			let timeLimit = 90;

			let infusions = [
				{
					deviceId: '3e0022000c47353136383631',
					accessToken: '1e43056f563df6c892b932875ca1e3c03efaca75',
					location: '',
					weight: 0,
					counter: 0,
					online: null,
					initialized: null,
					ready: false,
					debug: false
				},
				{
					deviceId: '123456789012345678901234',
					accessToken: 'demo',
					location: 'Demo location 1',
					weight: 0,
					counter: Math.floor(Math.random() * 600),
					online: null,
					initialized: null,
					ready: false,
					debug: false
				},
				{
					deviceId: '098765432109876543210987',
					accessToken: 'demo',
					location: 'Demo location 2',
					weight: 0,
					counter: Math.floor(Math.random() * 600),
					online: null,
					initialized: null,
					ready: false,
					debug: false
				}
			];

			function RefreshFromCloud() {
				for(i in infusions) {
					if('demo' == infusions[i].accessToken) {
						// Debug mode
						infusions[i].debug = true;
            infusions[i].ready = true;
            infusions[i].online = true;
            infusions[i].initialized = true;
            let newCounter = infusions[i].counter + 10;
            if(newCounter >= 10*60) newCounter = 0;
            infusions[i].weight = 1;
            infusions[i].counter = newCounter;
					} else {
						// Normal mode
						axios.get ("https://api.particle.io/v1/devices/"
											+ infusions[i].deviceId
											+ "/serialized",
						{
							params:
							{
								access_token: infusions[i].accessToken
							},
							timeout: 1000
						})
					  .then ((function (i, response) {
							// Deserialize response
							console.log(response);
							if(response.data.result.length >= 25) {
								let deserialized = DeserializeResponse(response.data.result);
								infusions[i] = {...infusions[i], ...deserialized, online: true, ready: true};
								console.log("Fetch successfull");
								console.log(infusions[i]);
							}
					  }).bind(this, i))
					  .catch ((function (i, error) {
					   	infusions[i] = {...infusions[i], online: false, ready: true};
							console.log("Fetch failed");
							console.log(error);
							console.log(infusions[i]);
					  }).bind(this, i));
					}
				}
			}

			function DeserializeResponse(serialized) {
				let weight = parseFloat(serialized.substr(0,16).trim());
				let counter = parseInt(serialized.substr(16,8).trim());
				let initialized = serialized.charAt(24) == '1';
				let location = initialized ? serialized.substr(25) : "";

				return {weight, counter, initialized, location};
			}

			function RenderItems() {
				let output = "";
				let id = 1;
				for(i in infusions) {
					infusion = infusions[i];
					output += `
					<div class="item" id="infusion-item-${id}">
						<div id="infusion-${id}" style="display: none">
							<img id="infusion-image-${id}" src="infusion.png" alt="item">
							<h2>Id: <span id="infusion-id-${id}"></span></h2>
							<p>Ort: <em style="float: right"><span id="infusion-location-${id}"></span></em>
							</p>
							<p>Status: <em style="float: right"><span id="infusion-status-${id}"><span></em>
							</p>

							<label class="trigger" id="infusion-button-${id}" for="popup__${id}"></label>
						</div>
						<div class="loader" id="infusion-load-${id}"></div>
					</div>

					<input type="checkbox" id="popup__${id}" class="popup__check" />
					<div class="popup__base">
						<label for="popup__${id}" class="popup__bg"></label>
						<div class="popup__inner">
							<div class="popup__calign">
								<label for="popup__${id}" class="popup__close">+</label>
							</div>
							<div class="popup__textbox">
								<h1><span id="popup-headline-${id}"></span></h1>
								<p>
									<u>Trage hier die Werte ein:</u>
								<p>
								<p>
									Ort: <input type="Text" id="input-location-${id}"/>
								</p>
								<br/>
								<button type="button" id="input-button-${id}">bernehmen</button>
							</div>
						</div>
					</div>
					`;
					id++;
				}
				let container = document.getElementById('item-container');
				container.innerHTML = output;
			}

			RenderItems();

			function Update() {
				RefreshFromCloud();

				let id = 1;
				for(i in infusions) {
					infusion = infusions[i];
					if(infusion.ready) {
						// Hide loading animation
						let loader = document.getElementById(`infusion-load-${id}`);
						loader.style.display = "none";

						let infusionBlock = document.getElementById(`infusion-${id}`);
						infusionBlock.style.display = "block";

						let infusionId = document.getElementById(`infusion-id-${id}`);
						infusionId.innerHTML = infusion.deviceId;

						if(infusion.online) {
							let inputButton = document.getElementById(`input-button-${id}`);
							inputButton.onclick = (function(j) {
								let inputLocation = document.getElementById(`input-location-${j}`);
								let newLocation = inputLocation.value;

								if((newLocation = newLocation.trim()) != "") {
									// Send new value to device ...
									infusions[j-1] = {...infusions[j-1], location: newLocation};
									if(!infusions[j-1].debug)
										axios.post ("https://api.particle.io/v1/devices/"
															+ infusions[j-1].deviceId
															+ "/init?access_token="
															+ encodeURI(infusions[j-1].accessToken),
										`arg=${infusions[j-1].location}`,
										{
											headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
											timeout: 5000
										})
									  .then (function (response) {
									  })
									  .catch (function (error) {
									  });

										// Collapse popup
										let checkbox = document.getElementById(`popup__${j}`);
										checkbox.checked = false;
								} else {
									alert("Unvollstndige Eingabe!");
								}

							}).bind(this, id);

							if(infusion.initialized) {
									let infusionButton = document.getElementById(`infusion-button-${id}`);
									infusionButton.innerHTML = "ndern";
									infusionButton.className = "trigger";

									let popupHeadline = document.getElementById(`popup-headline-${id}`);
									popupHeadline.innerHTML = "ndern";

									let inputLocation = document.getElementById(`input-location-${id}`);
									inputLocation.placeholder = infusion.location;


									let infusionStatus = document.getElementById(`infusion-status-${id}`);
									if(infusion.counter < 60) {
					            infusionStatus.innerHTML = "Infusion luft";
					        } else {
					            let minutes = Math.floor(infusion.counter / 60);
					            if(minutes == 1) {
					                infusionStatus.innerHTML = "Seit " + minutes + " Minute leer";
					            } else {
					                infusionStatus.innerHTML = "Seit " + minutes + " Minuten leer";
					            }
					        }


									// Calculate color.
					        // Starts with green, transforms to red over yellow.
					        let red = 0, green = 0, blue = 0;
					        let maxTime = Math.floor(timeLimit*3/4); // Full red after 3 quarter of time limit
					        let time = infusion.counter - 60;
					        if(time < 0) {
					            time = 0;
					        } else if(time > maxTime) {
					            time = maxTime;
					        }
					        let f = time / maxTime; // convert to [0 ... 1]
					        let a = (1.0 - f)*2.0;
					        let x = Math.floor(a);
					        let y = Math.floor(255.0*(a-x));
					        let group = Math.floor(x); // Group: 0->red 1->yellow 2->green
					        switch(group)
					        {
					            case 0:
					                red=255;
					                green=y;
					                blue=0;
					                break;
					            case 1:
					                red=255-y;
					                green=255;
					                blue=0;
					                break;
					            case 2:
					                red=0;
					                green=255;
					                blue=y;
					                break;
					        }
									let infusionItem = document.getElementById(`infusion-item-${id}`);
									infusionItem.style.backgroundColor = `rgb(${red},${green},${blue})`;


									let infusionLocation = document.getElementById(`infusion-location-${id}`);
									infusionLocation.innerHTML = infusion.location;
							} else {
								let infusionButton = document.getElementById(`infusion-button-${id}`);
								infusionButton.innerHTML = "Einrichten";

								let popupHeadline = document.getElementById(`popup-headline-${id}`);
								popupHeadline.innerHTML = "Einrichten";

								let infusionStatus = document.getElementById(`infusion-status-${id}`);
								infusionStatus.innerHTML = "Nicht eingerichtet";

								let infusionLocation = document.getElementById(`infusion-location-${id}`);
								infusionLocation.innerHTML = "---";
							}
						} else {
							let infusionButton = document.getElementById(`infusion-button-${id}`);
							infusionButton.innerHTML = "Einrichten";
							infusionButton.className = "disabled_trigger";

							let infusionStatus = document.getElementById(`infusion-status-${id}`);
							infusionStatus.innerHTML = "Offline";

							let infusionLocation = document.getElementById(`infusion-location-${id}`);
							infusionLocation.innerHTML = "---";
						}
					}

					id++;
				}
			}

			setInterval(function() {
			  Update();
			}, 1000);

			function OfflineAnimation(colorA, colorB) {
				for(let i=0; i<infusions.length; i++) {
					if(infusions[i].ready && !infusions[i].online) {
						let id = i+1;
						let infusionItem = document.getElementById(`infusion-item-${id}`);
						infusionItem.style.backgroundColor = colorA;
					}
				}
				setTimeout(OfflineAnimation.bind(this,colorB,colorA), 700);
			}

			OfflineAnimation("rgb(150,150,150)","rgb(255,255,255)");

		</script>

	</body></html>

Photon Application

C/C++
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Neo-Circle stuff (copy from: https://github.com/MrDio/IoT-Prototyping-Framework/tree/master/IoT-Eco-NeoPIXEL_Ring_Sample)
//

/**
 * 
 * Wiring for neo-pixel:
 * 
 * PHO (D2) - NEO (Data Input)
 * PHO (GND) - NEO (GND)
 * PHO (3V3) - NEO (5V)
 * 
 */

// This #include statement was automatically added by the Particle IDE.
#include <neopixel.h>

#include "HX711ADC.h"
#include "application.h"
#include "math.h"

class IoTEcoSys_NeoPIXEL_Ring
{
private:

public:
    IoTEcoSys_NeoPIXEL_Ring(int pixel);
    IoTEcoSys_NeoPIXEL_Ring();
    bool init();
    int setGlRGB( String color);
    void neoRingClockWise(int circles, int del, String color);
    void neoRingFillClockWise(int showuptime, int del, String color);
    void neoRingDoubleCounterClockWise(int circles, int del, String color);
    void neoRingCounterClockWise(int circles, int del, String color);
    void changeColor(uint32_t color);
};

// IMPORTANT: Set pixel PIN, COUNT, and TYPE
// Supported pixel types: WS2812, WS2812B, WS2812B2, WS2811, TM1803, TM1829, SK6812RGBW
#define PIXEL_PIN D2
#define PIXEL_COUNT 16
#define PIXEL_TYPE SK6812RGBW
#define BRIGHTNESS 250 // 0 - 255

Adafruit_NeoPixel _ring = Adafruit_NeoPixel(PIXEL_COUNT, PIXEL_PIN, PIXEL_TYPE);
int p1[] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
int p2[] = {8,9,10,11,12,13,14,15,16,0,1,2,3,4,5,6,7};
int gl_red = 0;
int gl_green = 0;
int gl_blue = 0;

// Constructor inits NeoPIXEL-Ring related to pixels (default is pin 2)
/*!
  \param pixels amount of pixels on ring
  \return obj insctance of class
*/
IoTEcoSys_NeoPIXEL_Ring::IoTEcoSys_NeoPIXEL_Ring() {
}

IoTEcoSys_NeoPIXEL_Ring::IoTEcoSys_NeoPIXEL_Ring(int pixel) {
}

//! The init function - nothing to do in this class
/*!
*/
bool IoTEcoSys_NeoPIXEL_Ring::init(){
    Serial.begin(9600);

    _ring.setBrightness(BRIGHTNESS);
    _ring.begin();
    _ring.show(); // Initialize all pixels to 'off'
    return true;
}


// Set RGB
/*!
  \param color red, green, blue, white or off
  \return int err = 0, else = 1
*/
int IoTEcoSys_NeoPIXEL_Ring::setGlRGB( String color) {
  if(color == "red") {
    gl_green = 0;
    gl_red = 255;
    gl_blue = 0;
    //ring.Color(0, 255, 0); // GRB
  }
  else if(color == "green") {
    gl_green = 255;
    gl_red = 0;
    gl_blue = 0;
    //ring.Color(255, 0, 0); // GRB
  }
  else if(color == "blue") {
    gl_green = 0;
    gl_red = 0;
    gl_blue = 255;
    //ring.Color(0, 0, 255);
  }
  else if(color == "white") {
    gl_green = 255;
    gl_red = 255;
    gl_blue = 255;
    _ring.Color(gl_green, gl_red, gl_blue, 255); // GRB+W
  }
  else if(color == "off") {
    gl_green = 0;
    gl_red = 0;
    gl_blue = 0;
    _ring.Color(gl_green, gl_red, gl_blue); // GRB+W
  }
}


// NeoPIXEL ring rotate clockwise
/*!
  \param circles amount of circles to animate
  \param del delay in ms
  \param color red, green, blue, white or off
*/
void IoTEcoSys_NeoPIXEL_Ring::neoRingClockWise(int circles, int del, String color){

    for(int c=0;c<circles;c++){
       setGlRGB("off");
       changeColor(_ring.Color(gl_green, gl_red, gl_blue)); // GRB
       _ring.show();
       for(int i=0;i<16;i++){
          setGlRGB(color);
         _ring.setPixelColor(15-p1[i],_ring.Color(gl_green, gl_red, gl_blue));
          if(i > -1){
            _ring.setPixelColor(15-(p1[i]-1),_ring.Color(0, 0, 0));
          }
          _ring.show();
          delay(del);  
      }
    }
    changeColor(_ring.Color(0, 0, 0)); // GRB
    _ring.show();
}

// NeoPIXEL ring fill clockwise
/*!
  \param showuptime time to showup in ms
  \param del delay in ms
  \param color red, green, blue, white or off
*/
void IoTEcoSys_NeoPIXEL_Ring::neoRingFillClockWise(int showuptime, int del, String color){
    changeColor(_ring.Color(0, 0, 0)); // GRB
    _ring.show();
       for(int i=0;i<16;i++){
          setGlRGB(color);
          _ring.setPixelColor(15-p1[i],_ring.Color(gl_green, gl_red, gl_blue));
          if(i > -1){
          }
          _ring.show();
          delay(del);  
      }
    delay(showuptime);
    changeColor(_ring.Color(0, 0, 0)); // GRB
    _ring.show();
}

// NeoPIXEL ring double counter clockwise
/*!
  \param circles amount of circles to animate
  \param del delay in ms
  \param color red, green, blue, white or off
*/
void IoTEcoSys_NeoPIXEL_Ring::neoRingDoubleCounterClockWise(int circles, int del, String color){

    for(int c=0;c<circles;c++){
       changeColor(_ring.Color(0, 0, 0)); // GRB
       _ring.show();
       for(int i=0;i<16;i++){
          setGlRGB(color);
          _ring.setPixelColor(p1[i],_ring.Color(gl_green, gl_red, gl_blue));
          _ring.setPixelColor(p2[i],_ring.Color(gl_green, gl_red, gl_blue));
          if(i > -1){
            setGlRGB("off");
            _ring.setPixelColor(p1[i]-1,_ring.Color(gl_green, gl_red, gl_blue));
            _ring.setPixelColor(p2[i]-1,_ring.Color(gl_green, gl_red, gl_blue));
          }
          _ring.show();
          delay(del);  
      } 
    }
    changeColor(_ring.Color(0, 0, 0)); // GRB
    _ring.show();
}

// NeoPIXEL ring double clockwise
/*!
  \param circles amount of circles to animate
  \param del delay in ms
  \param color red, green, blue, white or off
*/
void IoTEcoSys_NeoPIXEL_Ring::neoRingCounterClockWise(int circles, int del, String color){

    for(int c=0;c<circles;c++){
       changeColor(_ring.Color(0, 0, 0)); // GRB
       _ring.show();
       for(int i=0;i<16;i++){
          setGlRGB(color); 
          _ring.setPixelColor(p1[i],_ring.Color(gl_green, gl_red, gl_blue));
          if(i > -1){
            setGlRGB("off");
            _ring.setPixelColor(p1[i]-1,_ring.Color(gl_green, gl_red, gl_blue));
          }
          _ring.show();
          delay(del);  
      } 
    }
    changeColor(_ring.Color(0, 0, 0)); // GRB
    _ring.show();
}


// NeoPIXEL ring change color
/*!
  \param color color to change
*/

void IoTEcoSys_NeoPIXEL_Ring::changeColor(uint32_t color) {
  for(uint16_t i=0; i < _ring.numPixels(); i++) {
    _ring.setPixelColor(i, color);
    _ring.show();
  }
}



/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// I.S.I. Stuff goes here:
//

// HX711.DOUT	- pin #A1
// HX711.PD_SCK	- pin #A0

HX711ADC scale(A1, A0);		// parameter "gain" is ommited; the default value 128 is used by the library

double scaleValue = 0.0;    // Reading from ADC


////////////////////////////////////////////////////////////////////
// Diskreter Tiefpass Filter (FIR)
#define N_FILTER 16

const double A[]={0.001,0.005,0.02,0.04,0.07,0.1,0.1,0.1,0.1,0.1,0.1,0.07,0.04,0.02,0.005,0.001};

double filteredValue = 0.0;

double filter(double x) {
    static bool init = false;
    static double w[N_FILTER];
    
    // Initialize w's
    if(false == init) {
        for(int i=0; i<N_FILTER; i++) {
            w[i] = 0.0;
        }
        init = true;
    }
    
    for(int i=N_FILTER-1; i>0; i--) {
        w[i] = w[i-1];
    }
    
    w[0] = x;
    
    double y = 0;
    for(int i=0; i<N_FILTER; i++) {
        y += (A[i] * w[i]);
    }
    
    return y;
}


////////////////////////////////////////////////////////////////////
//  Main Program
double deltaMin = 0.01;
double deltaResume = 0.030;
int timeLimit = 90;
int counter = 0;
int initialized = 0;
String location = "";
IoTEcoSys_NeoPIXEL_Ring ring(16);

////////////////////////////////////////////////////////////////////
//  Serialization
String serialized = "0000000000000000000000000";
void SerializeData() {
    serialized = String::format("%16.5f%8d%d%s", filteredValue, counter, initialized?1:0, location.c_str());
}

////////////////////////////////////////////////////////////////////
//  Initialization via cloud
int Initialize(String argLocation) {
    location = argLocation;
    initialized = true;
    return location.length();
}

void setup() {
  SerializeData();
  Particle.variable("serialized", serialized);
  Particle.variable("weight", scaleValue);
  Particle.variable("avgWeight", filteredValue);
  Particle.variable("counter", counter);
  Particle.variable("location", location);
  Particle.function("init", Initialize);
  scale.set_scale(57000.f); // Original value was 2280. Normalized to kg
  scale.tare();	
  
  // Initialize Ring
  ring.init();  
}

unsigned long seconds = 0;

void loop() {
    if(initialized) {
        // Initialized, run state machines
        static unsigned long lastMillis = millis();
        
        if(millis() - lastMillis >= 500) {
            Particle.syncTime();
            lastMillis = millis();
            
            static int halfSeconds = 0;
            if(++halfSeconds == 2) {
                halfSeconds = 0;
                
                // Every second
                seconds++;
            }
            
            // Every 500ms
            ReadInputs();
        }
        
        // Update statemachines very often
        StateMachineCounter();
        StateMachineInfusion();
    } else {
        // Not initialized, blink light
        uint32_t color;
        
        color = (uint32_t)(255<<16) | (uint32_t)(255<<8) | (uint32_t)(255);
        ring.changeColor(color);
        
        delay(500);
        
        color = 0;
        ring.changeColor(color);
        
        delay(500);
    }
    
    
    SerializeData();
}

////////////////////////////////////////////////////////////////////
//  Input processing
void ReadInputs() {
    scaleValue = scale.get_units(5);
    filteredValue = filter(scaleValue);
    scale.power_down();		// put the ADC in sleep mode
    delay(100);
    scale.power_up();
}

////////////////////////////////////////////////////////////////////
//  State machine "counter"
void StateMachineCounter() {
	// Machine states
	static enum { INIT, COUNT } state = INIT;

	// Active transition (see docs)
	static int transition = 0,
		previousTransition = 0;

	// Global variables

	// Handle states
	switch (state) {
	case INIT:
	{
		if (previousTransition) {
			// Entry action

		}
		else {
			// Do action


			// Check transition conditions
			transition = 1;
		}


		if (transition) {
			// Exit action

		}
	}
	break;

	case COUNT:
	{
		// Local variable
		static unsigned long t;

		if (previousTransition) {
			// Entry action
			t = seconds;
		}
		else {
			// Do action


			// Check transition conditions
			if (seconds - t >= 1) {
				transition = 2;
			}
		}


		if (transition) {
			// Exit action

		}
	}
	break;
	}

#ifdef TESTBENCH
	const char *state2str[] = {
		"INIT",
		"COUNT"
	};
	if (transition) {
		PrintTimestamp();
		std::cout << "StateMachine: Counter, State: " << state2str[state] << ", Transition: " << transition << std::endl;
	}
#endif

	// Handle transition
	switch (transition) {
	case 1:
		// Init -> Count
	{
		// Perform transition action

		// Change state
		state = COUNT;
	}
	break;

	case 2:
		// Count -> Count
	{
		// Perform transition action
		counter++;

		// Change state
		state = COUNT;
	}
	break;
	}

	previousTransition = transition;

	// Deactivate transition
	transition = 0;

}

////////////////////////////////////////////////////////////////////
//  State machine "infusion"
void StateMachineInfusion() {
	// Machine states
	static enum { INIT, RUNNING, STOPPED } state = INIT;

	// Active transition (see docs)
	static int transition = 0,
		previousTransition = 0;

	// Global variables
	static double m;

	// Handle states
	switch (state) {
	case INIT:
	{
		if (previousTransition) {
			// Entry action

		}
		else {
			// Do action


			// Check transition conditions
			transition = 1;
		}


		if (transition) {
			// Exit action

		}
	}
	break;

	case RUNNING:
	{
		// Local variable

		if (previousTransition) {
			// Entry action
			m = filteredValue;
			ChangeColorByTime(0);
		}
		else {
			// Do action


			// Check transition conditions
			if (counter > 30) {
				transition = 3;
			}

			if (filteredValue - m < -1.0 * deltaMin) {
				transition = 2;
			}

			if (filteredValue - m > 0) {
				transition = 6;
			}
		}


		if (transition) {
			// Exit action

		}
	}
	break;

	case STOPPED:
	{
		// Local variables
		static unsigned long t;

		if (previousTransition) {
			// Entry action
			m = filteredValue;
			t = seconds;
		}
		else {
			// Do action
			ChangeColorByTime(seconds - t);

			// Check transition conditions
			static unsigned long t0 = seconds;
			if (seconds - t0 >= 60) {
				// Every 60 seconds
				t0 = seconds;

				if (filteredValue - m < -1.0 * deltaResume) {
					transition = 5;
				}
			}

			if (filteredValue - m > 50) {
				transition = 4;
			}
		}


		if (transition) {
			// Exit action
			counter = 0;
		}
	}
	break;

	}

#ifdef TESTBENCH
	const char *state2str[] = {
		"INIT",
		"RUNNING",
		"STOPPED"
	};
	if (transition) {
		PrintTimestamp();
		std::cout << "StateMachine: Infusion, Counter: " << counter << ", State: " << state2str[state] << ", Transition: " << transition << std::endl;
	}
#endif

	// Handle transition
	switch (transition) {
	case 1:
		// Init -> Running
	{
		// Perform transition action

		// Change state
		state = RUNNING;
	}
	break;

	case 2:
		// Running -> Running
	{
		// Perform transition action
		counter = 0;

		// Change state
		state = RUNNING;
	}
	break;

	case 3:
		// Running -> Stopped
	{
		// Perform transition action

		// Change state
		state = STOPPED;
	}
	break;

	case 4:
		// Stopped -> Running
	{
		// Perform transition action

		// Change state
		state = RUNNING;
	}
	break;

	case 5:
		// Stopped -> Running
	{
		// Perform transition action

		// Change state
		state = RUNNING;
	}
	break;

	case 6:
		// Running -> Running
	{
		// Perform transition action
		counter = 0;

		// Change state
		state = RUNNING;
	}
	break;

	}

	previousTransition = transition;

	// Deactivate transition
	transition = 0;
}

////////////////////////////////////////////////////////////////////
//  Time to color conversion
void ChangeColorByTime(int t) {
    // Calculate color.
    // Starts with green, transforms to red over yellow.
    int red = 0, green = 0, blue = 0;
    int maxTime = timeLimit*3/4; // Full red after this time
    int currentTime = t;
    if(currentTime > maxTime) {
        currentTime = maxTime;
    }
    float f = (float)currentTime / maxTime; // convert to [0 ... 1]
    float a = (/*1.f - */f)*2.f;
    float x = floor(a);
    float y = floor(255.f*(a-x));
    int group = (int)x; // Group: 0->red 1->yellow 2->green
    switch(group)
    {
        case 0:
            red=255;
            green=(int)y;
            blue=0;
            break;
        case 1:
            red=255-(int)y;
            green=255;
            blue=0;
            break;
        case 2:
            red=0;
            green=255;
            blue=(int)y;
            break;
    }
    uint32_t color = (uint32_t)(red<<16) | (uint32_t)(green<<8) | (uint32_t)(blue);
    ring.changeColor(color);
}

Filter Photon

C/C++
#include <stdio.h>

double scaleValue = 0.0;    // Reading from adc (scale)

// Diskreter Tiefpass Filter
#define N_FILTER 16

const double A[]={0.001,0.005,0.02,0.04,0.07,0.1,0.1,0.1,0.1,0.1,0.1,0.07,0.04,0.02,0.005,0.001};

double filteredValue = 0.0;

double filter(double x) {
    static double w[N_FILTER] = {
        0.0,
        0.0,
        0.0,
        0.0,
        0.0,
        0.0,
        0.0,
        0.0,
        0.0,
        0.0,
        0.0,
        0.0,
        0.0,
        0.0,
        0.0,
        0.0
    };
    
    for(int i=N_FILTER-1; i>0; i--) {
        w[i] = w[i-1];
    }
    
    w[0] = x;
    
    double y = 0;
    for(int i=0; i<N_FILTER; i++) {
        y += (A[i] * w[i]);
    }
    
    return y;
}

#include "mex.h"

void mexFunction( int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]){
	int samples = mxGetNumberOfElements(prhs[0]);
    double *x = mxGetData(prhs[0]);
    double *y = malloc(sizeof(double) * samples);
    
    
    for(int i=0; i<samples; i++) {
        y[i] = filter(x[i]);
        mexPrintf("%f => %f\n", x[i], y[i]);
    }
    
    plhs[0] = mxCreateDoubleMatrix(samples, 1, mxREAL);
    memcpy(mxGetPr(plhs[0]), y, samples * sizeof(double));
    free(y);
}

I.S.I Monitor

Android App

Credits

Kristian Madunic
1 project • 1 follower
Daniel Stümke
2 projects • 1 follower
Hodyra Daniel
1 project • 0 followers
Michael Koidis
1 project • 0 followers
A Team
1 project • 0 followers

Comments