Merge branch 'master' into add-rtc

This commit is contained in:
Mario Pesch 2021-06-11 10:10:51 +02:00
commit aef8a164ce
13 changed files with 738 additions and 286 deletions

View File

@ -9,6 +9,7 @@ import "./sensebox-display";
import "./sensebox-lora"; import "./sensebox-lora";
import "./sensebox-led"; import "./sensebox-led";
import "./sensebox-rtc"; import "./sensebox-rtc";
import "./sensebox-ble";
import "./sensebox-sd"; import "./sensebox-sd";
import "./mqtt"; import "./mqtt";
import "./text"; import "./text";

View File

@ -0,0 +1,145 @@
import * as Blockly from "blockly";
import { getColour } from "../helpers/colour";
Blockly.Blocks["sensebox_phyphox_init"] = {
init: function () {
this.setColour(getColour().phyphox);
this.appendDummyInput()
.appendField(Blockly.Msg.sensebox_phyphox_init)
.appendField(new Blockly.FieldTextInput("Geräte Name"), "devicename");
this.setPreviousStatement(true, null);
this.setNextStatement(true, null);
this.setTooltip(Blockly.Msg.sensebox_phyphox_init_tooltip);
},
};
Blockly.Blocks["sensebox_phyphox_experiment"] = {
init: function () {
this.setColour(getColour().phyphox);
this.appendDummyInput().appendField(
Blockly.Msg.sensebox_phyphox_createExperiment
);
this.appendDummyInput()
.appendField(Blockly.Msg.sensebox_phyphox_experimentTitle)
.appendField(new Blockly.FieldTextInput("Experiment Title"), "title");
this.appendDummyInput()
.appendField(Blockly.Msg.sensebox_phyphox_experimentDescription)
.appendField(
new Blockly.FieldTextInput(
Blockly.Msg.sensebox_phyphox_experiment_description
),
"description"
);
this.appendStatementInput("view").appendField(
Blockly.Msg.sensebox_phyphox_createView
);
this.setPreviousStatement(true, null);
this.setNextStatement(true, null);
this.setTooltip(Blockly.Msg.sensebox_phyphox_experiment_tooltip);
},
};
Blockly.Blocks["sensebox_phyphox_graph"] = {
init: function () {
this.setColour(getColour().phyphox);
this.appendDummyInput()
.appendField(Blockly.Msg.sensebox_phyphox_createGraph)
.appendField(Blockly.Msg.sensebox_phyphox_graphLabel)
.appendField(new Blockly.FieldTextInput("Label"), "label");
this.appendDummyInput()
.appendField(Blockly.Msg.sensebox_phyphox_unitx)
.appendField(new Blockly.FieldTextInput("Unit X"), "unitx");
this.appendDummyInput()
.appendField(Blockly.Msg.sensebox_phyphox_unity)
.appendField(new Blockly.FieldTextInput("Unit Y"), "unity");
this.appendDummyInput()
.appendField(Blockly.Msg.sensebox_phyphox_labelx)
.appendField(new Blockly.FieldTextInput("Label X"), "labelx");
this.appendDummyInput()
.appendField(Blockly.Msg.sensebox_phyphox_labely)
.appendField(new Blockly.FieldTextInput("Label Y"), "labely");
this.appendDummyInput()
.appendField(Blockly.Msg.sensebox_phyphox_graphStyle)
.appendField(
new Blockly.FieldDropdown([
[Blockly.Msg.sensebox_phyphox_style_dots, "dots"],
[Blockly.Msg.sensebox_phyphox_style_line, "line"],
]),
"style"
);
this.appendValueInput("channel0").appendField(
Blockly.Msg.sensebox_phyphox_channel0
);
this.appendValueInput("channel1").appendField(
Blockly.Msg.sensebox_phyphox_channel1
);
this.setPreviousStatement(true, null);
this.setNextStatement(true, null);
this.setTooltip(Blockly.Msg.sensebox_phyphox_graph_tooltip);
},
};
Blockly.Blocks["sensebox_phyphox_timestamp"] = {
init: function () {
this.setColour(getColour().phyphox);
this.appendDummyInput().appendField(Blockly.Msg.sensebox_phyphox_timestamp);
this.setOutput(true);
this.setTooltip(Blockly.Msg.sensebox_phyphox_timestamp_tooltip);
},
};
Blockly.Blocks["sensebox_phyphox_channel"] = {
init: function () {
this.setColour(getColour().phyphox);
this.appendDummyInput()
.appendField(Blockly.Msg.sensebox_phyphox_channel)
.appendField(
new Blockly.FieldDropdown([
["1", "1"],
["2", "2"],
["3", "3"],
["4", "4"],
["5", "5"],
]),
"channel"
);
this.setOutput(true);
this.setTooltip(Blockly.Msg.sensebox_phyphox_channel_tooltip);
},
};
Blockly.Blocks["sensebox_phyphox_sendchannel"] = {
init: function () {
this.setColour(getColour().phyphox);
this.appendValueInput("value")
.appendField(Blockly.Msg.sensebox_phyphox_sendchannel)
.appendField(
new Blockly.FieldDropdown([
["1", "1"],
["2", "2"],
["3", "3"],
["4", "4"],
["5", "5"],
]),
"channel"
);
this.setPreviousStatement(true, null);
this.setNextStatement(true, null);
this.setTooltip(Blockly.Msg.sensebox_phyphox_sendchannel_tooltip);
},
};
Blockly.Blocks["sensebox_phyphox_experiment_send"] = {
init: function () {
this.setColour(getColour().phyphox);
this.appendStatementInput("sendValues").appendField(
Blockly.Msg.sensebox_phyphox_writeValues
);
this.setPreviousStatement(true, null);
this.setNextStatement(true, null);
this.setTooltip(Blockly.Msg.sensebox_phyphox_experiment_send_tooltip);
},
};

View File

@ -100,6 +100,9 @@ Blockly["Arduino"].init = function (workspace) {
// creates a list of code to be setup before the setup block // creates a list of code to be setup before the setup block
Blockly["Arduino"].setupCode_ = Object.create(null); Blockly["Arduino"].setupCode_ = Object.create(null);
// creates a list of code to be setup before the setup block
Blockly["Arduino"].phyphoxSetupCode_ = Object.create(null);
// creates a list of code to be setup before the setup block // creates a list of code to be setup before the setup block
Blockly["Arduino"].loraSetupCode_ = Object.create(null); Blockly["Arduino"].loraSetupCode_ = Object.create(null);
@ -196,6 +199,7 @@ Blockly["Arduino"].finish = function (code) {
let codeFunctions = ""; let codeFunctions = "";
let functionsCode = ""; let functionsCode = "";
let definitionsCode = ""; let definitionsCode = "";
let phyphoxSetupCode = "";
let loopCodeOnce = ""; let loopCodeOnce = "";
let setupCode = ""; let setupCode = "";
let preSetupCode = ""; let preSetupCode = "";
@ -236,6 +240,18 @@ Blockly["Arduino"].finish = function (code) {
setupCode = setupCode =
"\nvoid setup() { \n" + preSetupCode + "\n" + loraSetupCode + "\n}\n"; "\nvoid setup() { \n" + preSetupCode + "\n" + loraSetupCode + "\n}\n";
for (const key in Blockly["Arduino"].phyphoxSetupCode_) {
phyphoxSetupCode += Blockly["Arduino"].phyphoxSetupCode_[key] + "\n" || "";
}
setupCode =
"\nvoid setup() { \n" +
preSetupCode +
"\n" +
phyphoxSetupCode +
"\n" +
loraSetupCode +
"\n}\n";
let loopCode = "\nvoid loop() { \n" + loopCodeOnce + code + "\n}\n"; let loopCode = "\nvoid loop() { \n" + loopCodeOnce + code + "\n}\n";

View File

@ -8,6 +8,7 @@ import "./sensebox-display";
import "./sensebox-lora"; import "./sensebox-lora";
import "./sensebox-led"; import "./sensebox-led";
import "./sensebox-rtc"; import "./sensebox-rtc";
import "./sensebox-ble";
import "./sensebox-sd"; import "./sensebox-sd";
import "./mqtt"; import "./mqtt";
import "./logic"; import "./logic";

View File

@ -1,5 +1,4 @@
import * as Blockly from "blockly/core";
import * as Blockly from 'blockly/core';
/** /**
* @license Licensed under the Apache License, Version 2.0 (the "License"): * @license Licensed under the Apache License, Version 2.0 (the "License"):
@ -19,15 +18,15 @@ import * as Blockly from 'blockly/core';
* @param {!Blockly.Block} block Block to generate the code from. * @param {!Blockly.Block} block Block to generate the code from.
* @return {array} Completed code with order of operation. * @return {array} Completed code with order of operation.
*/ */
Blockly.Arduino['math_number'] = function (block) { Blockly.Arduino["math_number"] = function (block) {
// Numeric value. // Numeric value.
var code = parseFloat(block.getFieldValue('NUM')); var code = parseFloat(block.getFieldValue("NUM"));
if (code === Infinity) { if (code === Infinity) {
code = 'INFINITY'; code = "INFINITY";
} else if (code === -Infinity) { } else if (code === -Infinity) {
code = '-INFINITY'; code = "-INFINITY";
} }
return [code, Blockly.Arduino.ORDER_ATOMIC]; return [code, Blockly.Arduino.ORDER_ATOMIC];
}; };
/** /**
@ -37,27 +36,27 @@ Blockly.Arduino['math_number'] = function (block) {
* @param {!Blockly.Block} block Block to generate the code from. * @param {!Blockly.Block} block Block to generate the code from.
* @return {array} Completed code with order of operation. * @return {array} Completed code with order of operation.
*/ */
Blockly.Arduino['math_arithmetic'] = function (block) { Blockly.Arduino["math_arithmetic"] = function (block) {
var OPERATORS = { var OPERATORS = {
ADD: [' + ', Blockly.Arduino.ORDER_ADDITIVE], ADD: [" + ", Blockly.Arduino.ORDER_ADDITIVE],
MINUS: [' - ', Blockly.Arduino.ORDER_ADDITIVE], MINUS: [" - ", Blockly.Arduino.ORDER_ADDITIVE],
MULTIPLY: [' * ', Blockly.Arduino.ORDER_MULTIPLICATIVE], MULTIPLY: [" * ", Blockly.Arduino.ORDER_MULTIPLICATIVE],
DIVIDE: [' / ', Blockly.Arduino.ORDER_MULTIPLICATIVE], DIVIDE: [" / ", Blockly.Arduino.ORDER_MULTIPLICATIVE],
POWER: [null, Blockly.Arduino.ORDER_NONE] // Handle power separately. POWER: [null, Blockly.Arduino.ORDER_NONE], // Handle power separately.
}; };
var tuple = OPERATORS[block.getFieldValue('OP')]; var tuple = OPERATORS[block.getFieldValue("OP")];
var operator = tuple[0]; var operator = tuple[0];
var order = tuple[1]; var order = tuple[1];
var argument0 = Blockly.Arduino.valueToCode(block, 'A', order) || '0'; var argument0 = Blockly.Arduino.valueToCode(block, "A", order) || "0";
var argument1 = Blockly.Arduino.valueToCode(block, 'B', order) || '0'; var argument1 = Blockly.Arduino.valueToCode(block, "B", order) || "0";
var code; var code;
// Power in C++ requires a special case since it has no operator. // Power in C++ requires a special case since it has no operator.
if (!operator) { if (!operator) {
code = 'Math.pow(' + argument0 + ', ' + argument1 + ')'; code = "Math.pow(" + argument0 + ", " + argument1 + ")";
return [code, Blockly.Arduino.ORDER_UNARY_POSTFIX]; return [code, Blockly.Arduino.ORDER_UNARY_POSTFIX];
} }
code = argument0 + operator + argument1; code = argument0 + operator + argument1;
return [code, order]; return [code, order];
}; };
/** /**
@ -66,90 +65,103 @@ Blockly.Arduino['math_arithmetic'] = function (block) {
* @param {!Blockly.Block} block Block to generate the code from. * @param {!Blockly.Block} block Block to generate the code from.
* @return {array} Completed code with order of operation. * @return {array} Completed code with order of operation.
*/ */
Blockly.Arduino['math_single'] = function (block) { Blockly.Arduino["math_single"] = function (block) {
var operator = block.getFieldValue('OP'); var operator = block.getFieldValue("OP");
var code; var code;
var arg; var arg;
if (operator === 'NEG') { if (operator === "NEG") {
// Negation is a special case given its different operator precedents. // Negation is a special case given its different operator precedents.
arg = Blockly.Arduino.valueToCode(block, 'NUM', arg =
Blockly.Arduino.ORDER_UNARY_PREFIX) || '0'; Blockly.Arduino.valueToCode(
if (arg[0] === '-') { block,
// --3 is not legal in C++ in this context. "NUM",
arg = ' ' + arg; Blockly.Arduino.ORDER_UNARY_PREFIX
} ) || "0";
code = '-' + arg; if (arg[0] === "-") {
return [code, Blockly.Arduino.ORDER_UNARY_PREFIX]; // --3 is not legal in C++ in this context.
arg = " " + arg;
} }
if (operator === 'ABS' || operator.substring(0, 5) === 'ROUND') { code = "-" + arg;
arg = Blockly.Arduino.valueToCode(block, 'NUM', return [code, Blockly.Arduino.ORDER_UNARY_PREFIX];
Blockly.Arduino.ORDER_UNARY_POSTFIX) || '0'; }
} else if (operator === 'SIN' || operator === 'COS' || operator === 'TAN') { if (operator === "ABS" || operator.substring(0, 5) === "ROUND") {
arg = Blockly.Arduino.valueToCode(block, 'NUM', arg =
Blockly.Arduino.ORDER_MULTIPLICATIVE) || '0'; Blockly.Arduino.valueToCode(
} else { block,
arg = Blockly.Arduino.valueToCode(block, 'NUM', "NUM",
Blockly.Arduino.ORDER_NONE) || '0'; Blockly.Arduino.ORDER_UNARY_POSTFIX
} ) || "0";
// First, handle cases which generate values that don't need parentheses. } else if (operator === "SIN" || operator === "COS" || operator === "TAN") {
switch (operator) { arg =
case 'ABS': Blockly.Arduino.valueToCode(
code = 'abs(' + arg + ')'; block,
break; "NUM",
case 'ROOT': Blockly.Arduino.ORDER_MULTIPLICATIVE
code = 'sqrt(' + arg + ')'; ) || "0";
break; } else {
case 'LN': arg =
code = 'log(' + arg + ')'; Blockly.Arduino.valueToCode(block, "NUM", Blockly.Arduino.ORDER_NONE) ||
break; "0";
case 'EXP': }
code = 'exp(' + arg + ')'; // First, handle cases which generate values that don't need parentheses.
break; switch (operator) {
case 'POW10': case "ABS":
code = 'pow(10,' + arg + ')'; code = "abs(" + arg + ")";
break; break;
case 'ROUND': case "ROOT":
code = 'round(' + arg + ')'; code = "sqrt(" + arg + ")";
break; break;
case 'ROUNDUP': case "LN":
code = 'ceil(' + arg + ')'; code = "log(" + arg + ")";
break; break;
case 'ROUNDDOWN': case "EXP":
code = 'floor(' + arg + ')'; code = "exp(" + arg + ")";
break; break;
case 'SIN': case "POW10":
code = 'sin(' + arg + ' / 180 * Math.PI)'; code = "pow(10," + arg + ")";
break; break;
case 'COS': case "ROUND":
code = 'cos(' + arg + ' / 180 * Math.PI)'; code = "round(" + arg + ")";
break; break;
case 'TAN': case "ROUNDUP":
code = 'tan(' + arg + ' / 180 * Math.PI)'; code = "ceil(" + arg + ")";
break; break;
default: case "ROUNDDOWN":
break; code = "floor(" + arg + ")";
} break;
if (code) { case "SIN":
return [code, Blockly.Arduino.ORDER_UNARY_POSTFIX]; code = "sin(" + arg + " / 180 * Math.PI)";
} break;
// Second, handle cases which generate values that may need parentheses. case "COS":
switch (operator) { code = "cos(" + arg + " / 180 * Math.PI)";
case 'LOG10': break;
code = 'log(' + arg + ') / log(10)'; case "TAN":
break; code = "tan(" + arg + " / 180 * Math.PI)";
case 'ASIN': break;
code = 'asin(' + arg + ') / M_PI * 180'; default:
break; break;
case 'ACOS': }
code = 'acos(' + arg + ') / M_PI * 180'; if (code) {
break; return [code, Blockly.Arduino.ORDER_UNARY_POSTFIX];
case 'ATAN': }
code = 'atan(' + arg + ') / M_PI * 180'; // Second, handle cases which generate values that may need parentheses.
break; switch (operator) {
default: case "LOG10":
throw new Error('Unknown math operator: ' + operator); code = "log(" + arg + ") / log(10)";
} break;
return [code, Blockly.Arduino.ORDER_MULTIPLICATIVE]; case "ASIN":
code = "asin(" + arg + ") / M_PI * 180";
break;
case "ACOS":
code = "acos(" + arg + ") / M_PI * 180";
break;
case "ATAN":
code = "atan(" + arg + ") / M_PI * 180";
break;
default:
throw new Error("Unknown math operator: " + operator);
}
return [code, Blockly.Arduino.ORDER_MULTIPLICATIVE];
}; };
/** /**
@ -161,16 +173,16 @@ Blockly.Arduino['math_single'] = function (block) {
* @param {!Blockly.Block} block Block to generate the code from. * @param {!Blockly.Block} block Block to generate the code from.
* @return {string} Completed code. * @return {string} Completed code.
*/ */
Blockly.Arduino['math_constant'] = function (block) { Blockly.Arduino["math_constant"] = function (block) {
var CONSTANTS = { var CONSTANTS = {
'PI': ['M_PI', Blockly.Arduino.ORDER_UNARY_POSTFIX], PI: ["M_PI", Blockly.Arduino.ORDER_UNARY_POSTFIX],
'E': ['M_E', Blockly.Arduino.ORDER_UNARY_POSTFIX], E: ["M_E", Blockly.Arduino.ORDER_UNARY_POSTFIX],
'GOLDEN_RATIO': ['(1 + sqrt(5)) / 2', Blockly.Arduino.ORDER_MULTIPLICATIVE], GOLDEN_RATIO: ["(1 + sqrt(5)) / 2", Blockly.Arduino.ORDER_MULTIPLICATIVE],
'SQRT2': ['M_SQRT2', Blockly.Arduino.ORDER_UNARY_POSTFIX], SQRT2: ["M_SQRT2", Blockly.Arduino.ORDER_UNARY_POSTFIX],
'SQRT1_2': ['M_SQRT1_2', Blockly.Arduino.ORDER_UNARY_POSTFIX], SQRT1_2: ["M_SQRT1_2", Blockly.Arduino.ORDER_UNARY_POSTFIX],
'INFINITY': ['INFINITY', Blockly.Arduino.ORDER_ATOMIC] INFINITY: ["INFINITY", Blockly.Arduino.ORDER_ATOMIC],
}; };
return CONSTANTS[block.getFieldValue('CONSTANT')]; return CONSTANTS[block.getFieldValue("CONSTANT")];
}; };
/** /**
@ -180,63 +192,72 @@ Blockly.Arduino['math_constant'] = function (block) {
* @param {!Blockly.Block} block Block to generate the code from. * @param {!Blockly.Block} block Block to generate the code from.
* @return {array} Completed code with order of operation. * @return {array} Completed code with order of operation.
*/ */
Blockly.Arduino['math_number_property'] = function (block) { Blockly.Arduino["math_number_property"] = function (block) {
var number_to_check = Blockly.Arduino.valueToCode(block, 'NUMBER_TO_CHECK', var number_to_check =
Blockly.Arduino.ORDER_MULTIPLICATIVE) || '0'; Blockly.Arduino.valueToCode(
var dropdown_property = block.getFieldValue('PROPERTY'); block,
var code; "NUMBER_TO_CHECK",
if (dropdown_property === 'PRIME') { Blockly.Arduino.ORDER_MULTIPLICATIVE
var func = [ ) || "0";
'boolean ' + Blockly.Arduino.DEF_FUNC_NAME + '(int n) {', var dropdown_property = block.getFieldValue("PROPERTY");
' // https://en.wikipedia.org/wiki/Primality_test#Naive_methods', var code;
' if (n == 2 || n == 3) {', if (dropdown_property === "PRIME") {
' return true;', var func = [
' }', "boolean " + Blockly.Arduino.DEF_FUNC_NAME + "(int n) {",
' // False if n is NaN, negative, is 1.', " // https://en.wikipedia.org/wiki/Primality_test#Naive_methods",
' // And false if n is divisible by 2 or 3.', " if (n == 2 || n == 3) {",
' if (isnan(n) || (n <= 1) || (n == 1) || (n % 2 == 0) || ' + " return true;",
'(n % 3 == 0)) {', " }",
' return false;', " // False if n is NaN, negative, is 1.",
' }', " // And false if n is divisible by 2 or 3.",
' // Check all the numbers of form 6k +/- 1, up to sqrt(n).', " if (isnan(n) || (n <= 1) || (n == 1) || (n % 2 == 0) || " +
' for (int x = 6; x <= sqrt(n) + 1; x += 6) {', "(n % 3 == 0)) {",
' if (n % (x - 1) == 0 || n % (x + 1) == 0) {', " return false;",
' return false;', " }",
' }', " // Check all the numbers of form 6k +/- 1, up to sqrt(n).",
' }', " for (int x = 6; x <= sqrt(n) + 1; x += 6) {",
' return true;', " if (n % (x - 1) == 0 || n % (x + 1) == 0) {",
'}']; " return false;",
var funcName = Blockly.Arduino.addFunction('mathIsPrime', func.join('\n')); " }",
Blockly.Arduino.addInclude('math', '#include <math.h>'); " }",
code = funcName + '(' + number_to_check + ')'; " return true;",
return [code, Blockly.Arduino.ORDER_UNARY_POSTFIX]; "}",
} ];
switch (dropdown_property) { var funcName = Blockly.Arduino.addFunction("mathIsPrime", func.join("\n"));
case 'EVEN': Blockly.Arduino.addInclude("math", "#include <math.h>");
code = number_to_check + ' % 2 == 0'; code = funcName + "(" + number_to_check + ")";
break; return [code, Blockly.Arduino.ORDER_UNARY_POSTFIX];
case 'ODD': }
code = number_to_check + ' % 2 == 1'; switch (dropdown_property) {
break; case "EVEN":
case 'WHOLE': code = number_to_check + " % 2 == 0";
Blockly.Arduino.addInclude('math', '#include <math.h>'); break;
code = '(floor(' + number_to_check + ') == ' + number_to_check + ')'; case "ODD":
break; code = number_to_check + " % 2 == 1";
case 'POSITIVE': break;
code = number_to_check + ' > 0'; case "WHOLE":
break; Blockly.Arduino.addInclude("math", "#include <math.h>");
case 'NEGATIVE': code = "(floor(" + number_to_check + ") == " + number_to_check + ")";
code = number_to_check + ' < 0'; break;
break; case "POSITIVE":
case 'DIVISIBLE_BY': code = number_to_check + " > 0";
var divisor = Blockly.Arduino.valueToCode(block, 'DIVISOR', break;
Blockly.Arduino.ORDER_MULTIPLICATIVE) || '0'; case "NEGATIVE":
code = number_to_check + ' % ' + divisor + ' == 0'; code = number_to_check + " < 0";
break; break;
default: case "DIVISIBLE_BY":
break; var divisor =
} Blockly.Arduino.valueToCode(
return [code, Blockly.Arduino.ORDER_EQUALITY]; block,
"DIVISOR",
Blockly.Arduino.ORDER_MULTIPLICATIVE
) || "0";
code = number_to_check + " % " + divisor + " == 0";
break;
default:
break;
}
return [code, Blockly.Arduino.ORDER_EQUALITY];
}; };
/** /**
@ -247,19 +268,25 @@ Blockly.Arduino['math_number_property'] = function (block) {
* @param {!Blockly.Block} block Block to generate the code from. * @param {!Blockly.Block} block Block to generate the code from.
* @return {array} Completed code with order of operation. * @return {array} Completed code with order of operation.
*/ */
Blockly.Arduino['math_change'] = function (block) { Blockly.Arduino["math_change"] = function (block) {
var argument0 = Blockly.Arduino.valueToCode(block, 'DELTA', var argument0 =
Blockly.Arduino.ORDER_ADDITIVE) || '0'; Blockly.Arduino.valueToCode(
var varName = Blockly.Arduino.variableDB_.getName( block,
block.getFieldValue('VAR'), Blockly.Variables.NAME_TYPE); "DELTA",
return varName + ' += ' + argument0 + ';\n'; Blockly.Arduino.ORDER_ADDITIVE
) || "0";
var varName = Blockly.Arduino.variableDB_.getName(
block.getFieldValue("VAR"),
Blockly.Variables.NAME_TYPE
);
return varName + " += " + argument0 + ";\n";
}; };
/** Rounding functions have a single operand. */ /** Rounding functions have a single operand. */
Blockly.Arduino['math_round'] = Blockly.Arduino['math_single']; Blockly.Arduino["math_round"] = Blockly.Arduino["math_single"];
/** Trigonometry functions have a single operand. */ /** Trigonometry functions have a single operand. */
Blockly.Arduino['math_trig'] = Blockly.Arduino['math_single']; Blockly.Arduino["math_trig"] = Blockly.Arduino["math_single"];
/** /**
* Generator for the math function to a list. * Generator for the math function to a list.
@ -268,7 +295,7 @@ Blockly.Arduino['math_trig'] = Blockly.Arduino['math_single'];
* @param {!Blockly.Block} block Block to generate the code from. * @param {!Blockly.Block} block Block to generate the code from.
* @return {array} Completed code with order of operation. * @return {array} Completed code with order of operation.
*/ */
Blockly.Arduino['math_on_list'] = Blockly.Arduino.noGeneratorCodeInline; Blockly.Arduino["math_on_list"] = Blockly.Arduino.noGeneratorCodeInline;
/** /**
* Generator for the math modulo function (calculates remainder of X/Y). * Generator for the math modulo function (calculates remainder of X/Y).
@ -276,13 +303,21 @@ Blockly.Arduino['math_on_list'] = Blockly.Arduino.noGeneratorCodeInline;
* @param {!Blockly.Block} block Block to generate the code from. * @param {!Blockly.Block} block Block to generate the code from.
* @return {array} Completed code with order of operation. * @return {array} Completed code with order of operation.
*/ */
Blockly.Arduino['math_modulo'] = function (block) { Blockly.Arduino["math_modulo"] = function (block) {
var argument0 = Blockly.Arduino.valueToCode(block, 'DIVIDEND', var argument0 =
Blockly.Arduino.ORDER_MULTIPLICATIVE) || '0'; Blockly.Arduino.valueToCode(
var argument1 = Blockly.Arduino.valueToCode(block, 'DIVISOR', block,
Blockly.Arduino.ORDER_MULTIPLICATIVE) || '0'; "DIVIDEND",
var code = argument0 + ' % ' + argument1; Blockly.Arduino.ORDER_MULTIPLICATIVE
return [code, Blockly.Arduino.ORDER_MULTIPLICATIVE]; ) || "0";
var argument1 =
Blockly.Arduino.valueToCode(
block,
"DIVISOR",
Blockly.Arduino.ORDER_MULTIPLICATIVE
) || "0";
var code = argument0 + " % " + argument1;
return [code, Blockly.Arduino.ORDER_MULTIPLICATIVE];
}; };
/** /**
@ -291,18 +326,34 @@ Blockly.Arduino['math_modulo'] = function (block) {
* @param {!Blockly.Block} block Block to generate the code from. * @param {!Blockly.Block} block Block to generate the code from.
* @return {array} Completed code with order of operation. * @return {array} Completed code with order of operation.
*/ */
Blockly.Arduino['math_constrain'] = function (block) { Blockly.Arduino["math_constrain"] = function (block) {
// Constrain a number between two limits. // Constrain a number between two limits.
var argument0 = Blockly.Arduino.valueToCode(block, 'VALUE', var argument0 =
Blockly.Arduino.ORDER_NONE) || '0'; Blockly.Arduino.valueToCode(block, "VALUE", Blockly.Arduino.ORDER_NONE) ||
var argument1 = Blockly.Arduino.valueToCode(block, 'LOW', "0";
Blockly.Arduino.ORDER_NONE) || '0'; var argument1 =
var argument2 = Blockly.Arduino.valueToCode(block, 'HIGH', Blockly.Arduino.valueToCode(block, "LOW", Blockly.Arduino.ORDER_NONE) ||
Blockly.Arduino.ORDER_NONE) || '0'; "0";
var code = '(' + argument0 + ' < ' + argument1 + ' ? ' + argument1 + var argument2 =
' : ( ' + argument0 + ' > ' + argument2 + ' ? ' + argument2 + ' : ' + Blockly.Arduino.valueToCode(block, "HIGH", Blockly.Arduino.ORDER_NONE) ||
argument0 + '))'; "0";
return [code, Blockly.Arduino.ORDER_UNARY_POSTFIX]; var code =
"(" +
argument0 +
" < " +
argument1 +
" ? " +
argument1 +
" : ( " +
argument0 +
" > " +
argument2 +
" ? " +
argument2 +
" : " +
argument0 +
"))";
return [code, Blockly.Arduino.ORDER_UNARY_POSTFIX];
}; };
/** /**
@ -312,28 +363,26 @@ Blockly.Arduino['math_constrain'] = function (block) {
* @param {!Blockly.Block} block Block to generate the code from. * @param {!Blockly.Block} block Block to generate the code from.
* @return {array} Completed code with order of operation. * @return {array} Completed code with order of operation.
*/ */
Blockly.Arduino['math_random_int'] = function (block) { Blockly.Arduino["math_random_int"] = function (block) {
var argument0 = Blockly.Arduino.valueToCode(block, 'FROM', var argument0 =
Blockly.Arduino.ORDER_NONE) || '0'; Blockly.Arduino.valueToCode(block, "FROM", Blockly.Arduino.ORDER_NONE) ||
var argument1 = Blockly.Arduino.valueToCode(block, 'TO', "0";
Blockly.Arduino.ORDER_NONE) || '0'; var argument1 =
var functionName = Blockly.Arduino.variableDB_.getDistinctName( Blockly.Arduino.valueToCode(block, "TO", Blockly.Arduino.ORDER_NONE) || "0";
'math_random_int', Blockly.Generator.NAME_TYPE); Blockly.Arduino.setupCode_["init_rand"] = "randomSeed(analogRead(0));";
Blockly.Arduino.setups_['init_rand'] = 'randomSeed(analogRead(0));'; Blockly.Arduino.functionNames_[
Blockly.Arduino.math_random_int.random_function = functionName; "math_random_int"
var func = [ ] = `int mathRandomInt (int min, int max) {\n
'int ' + Blockly.Arduino.DEF_FUNC_NAME + '(int min, int max) {', if (min > max) {
' if (min > max) {', int temp = min;
' // Swap min and max to ensure min is smaller.', min = max;
' int temp = min;', max = temp;
' min = max;', }
' max = temp;', return min + (rand() % (max - min + 1));
' }', }
' return min + (rand() % (max - min + 1));', `;
'}']; var code = `mathRandomInt(${argument0},${argument1});`;
var funcName = Blockly.Arduino.addFunction('mathRandomInt', func.join('\n')); return [code, Blockly.Arduino.ORDER_ATOMIC];
var code = funcName + '(' + argument0 + ', ' + argument1 + ')';
return [code, Blockly.Arduino.ORDER_UNARY_POSTFIX];
}; };
/** /**
@ -342,6 +391,6 @@ Blockly.Arduino['math_random_int'] = function (block) {
* @param {!Blockly.Block} block Block to generate the code from. * @param {!Blockly.Block} block Block to generate the code from.
* @return {string} Completed code. * @return {string} Completed code.
*/ */
Blockly.Arduino['math_random_float'] = function (block) { Blockly.Arduino["math_random_float"] = function (block) {
return ['(rand() / RAND_MAX)', Blockly.Arduino.ORDER_UNARY_POSTFIX]; return ["(rand() / RAND_MAX)", Blockly.Arduino.ORDER_UNARY_POSTFIX];
}; };

View File

@ -0,0 +1,127 @@
import * as Blockly from "blockly/core";
Blockly.Arduino.sensebox_phyphox_init = function () {
var name = this.getFieldValue("devicename");
Blockly.Arduino.libraries_["phyphox_library"] = `#include <phyphoxBle.h>`;
Blockly.Arduino.libraries_["library_senseBoxMCU"] =
'#include "SenseBoxMCU.h"';
Blockly.Arduino.phyphoxSetupCode_[
"phyphox_start"
] = `PhyphoxBLE::start("${name}");`;
var code = ``;
return code;
};
Blockly.Arduino.sensebox_phyphox_experiment = function () {
var experimentname = "experiment";
var title = this.getFieldValue("title").replace(/[^a-zA-Z0-9]/g, "");
var description = this.getFieldValue("description");
var branch = Blockly.Arduino.statementToCode(this, "view");
Blockly.Arduino.phyphoxSetupCode_[
`PhyphoxBleExperiment_${experimentname}`
] = `PhyphoxBleExperiment ${experimentname};`;
Blockly.Arduino.phyphoxSetupCode_[
`setTitle_${title}`
] = `${experimentname}.setTitle("${title}");`;
Blockly.Arduino.phyphoxSetupCode_[
`setCategory_senseBoxExperiments}`
] = `${experimentname}.setCategory("senseBox Experimente");`;
Blockly.Arduino.phyphoxSetupCode_[
`setDescription_${description}`
] = `${experimentname}.setDescription("${description}");`;
Blockly.Arduino.phyphoxSetupCode_[
`addView_${experimentname}`
] = `PhyphoxBleExperiment::View firstView;\nfirstView.setLabel("Messwerte"); //Create a "view"`;
Blockly.Arduino.phyphoxSetupCode_[`addGraph`] = `${branch}`;
Blockly.Arduino.phyphoxSetupCode_[
`addView_firstview`
] = `${experimentname}.addView(firstView);`; //Attach view to experiment
Blockly.Arduino.phyphoxSetupCode_[
`addExperiment_${experimentname}`
] = `PhyphoxBLE::addExperiment(${experimentname});`; //Attach experiment to server
var code = ``;
return code;
};
Blockly.Arduino["sensebox_phyphox_timestamp"] = function () {
var code = 0;
return [code, Blockly.Arduino.ORDER_ATOMIC];
};
Blockly.Arduino["sensebox_phyphox_channel"] = function () {
var channel = parseFloat(this.getFieldValue("channel"));
var code = channel;
return [code, Blockly.Arduino.ORDER_ATOMIC];
};
Blockly.Arduino.sensebox_phyphox_sendchannel = function (block) {
var channel = this.getFieldValue("channel");
var value =
Blockly.Arduino.valueToCode(this, "value", Blockly.Arduino.ORDER_ATOMIC) ||
"1";
var code = `float channel${channel} = ${value};\n`;
return code;
};
Blockly.Arduino.sensebox_phyphox_graph = function () {
var label = this.getFieldValue("label").replace(/[^a-zA-Z0-9]/g, "");
var unitx = this.getFieldValue("unitx");
var unity = this.getFieldValue("unity");
var labelx = this.getFieldValue("labelx");
var labely = this.getFieldValue("labely");
var style = this.getFieldValue("style");
var channelX =
Blockly.Arduino.valueToCode(
this,
"channel0",
Blockly.Arduino.ORDER_ATOMIC
) || 0;
var channelY =
Blockly.Arduino.valueToCode(
this,
"channel1",
Blockly.Arduino.ORDER_ATOMIC
) || 1;
var code = `PhyphoxBleExperiment::Graph ${label};\n`; //Create graph which will plot random numbers over time
code += `${label}.setLabel("${label}");\n`;
code += `${label}.setUnitX("${unitx}");\n`;
code += `${label}.setUnitY("${unity}");\n`;
code += `${label}.setLabelX("${labelx}");\n`;
code += `${label}.setLabelY("${labely}");\n`;
code += `${label}.setStyle("${style}");\n`;
code += `${label}.setChannel(${channelX}, ${channelY});\n`;
code += `firstView.addElement(${label});\n`;
return code;
};
Blockly.Arduino.sensebox_phyphox_experiment_send = function () {
var branch = Blockly.Arduino.statementToCode(this, "sendValues");
var blocks = this.getDescendants();
console.log(blocks);
var count = 0;
if (blocks !== undefined) {
for (var i = 0; i < blocks.length; i++) {
if (blocks[i].type === "sensebox_phyphox_sendchannel") {
count++;
}
}
}
if (count === 5) {
}
var string = "";
for (var j = 1; j <= count; j++) {
console.log("append");
if (string === "") {
string += `channel${j}`;
} else if (string !== "") {
string += `, channel${j}`;
}
}
Blockly.Arduino.loopCodeOnce_["phyphox_poll"] = `PhyphoxBLE::poll();`;
var code = `${branch}\nPhyphoxBLE::write(${string});`;
return code;
};

View File

@ -1,22 +1,20 @@
const colours = { const colours = {
sensebox: 120, sensebox: 120,
logic: 210, logic: 210,
loops: 10, loops: 10,
math: 230, math: 230,
io: 60, io: 60,
procedures: 290, procedures: 290,
time: 140, time: 140,
text: 160, text: 160,
variables: 330, variables: 330,
audio: 250, audio: 250,
arrays: 33, arrays: 33,
mqtt: 90, mqtt: 90,
webserver: 40 webserver: 40,
} phyphox: 25,
export const getColour = () => {
return colours;
}; };
export const getColour = () => {
return colours;
};

View File

@ -1,4 +1,5 @@
import { AUDIO } from "./de/audio"; import { AUDIO } from "./de/audio";
import { BLE } from "./de/sensebox-ble";
import { FAQ } from "./de/faq"; import { FAQ } from "./de/faq";
import { IO } from "./de/io"; import { IO } from "./de/io";
import { LOGIC } from "./de/logic"; import { LOGIC } from "./de/logic";
@ -24,6 +25,7 @@ import { WEBSERVER } from "./de/webserver";
export const De = { export const De = {
...AUDIO, ...AUDIO,
...BLE,
...FAQ, ...FAQ,
...IO, ...IO,
...LOGIC, ...LOGIC,

View File

@ -0,0 +1,41 @@
export const BLE = {
/**
* Phyphox Blöcke
*/
sensebox_phyphox_init: "Initialisiere Phyphox Gerät mit Namen:",
sensebox_phyphox_createExperiment: "Erstelle Experiment",
sensebox_phyphox_experimentName: "Name des Experiments",
sensebox_phyphox_experimentTitle: "Titel",
sensebox_phyphox_experimentCategory: "Kategorie",
sensebox_phyphox_experimentDescription: "Beschreibung",
sensebox_phyphox_experiment_description: "Kurze Beschreibung des Experiments",
sensebox_phyphox_writeValues: "Sende Werte",
sensebox_phyphox_createView: "Mit Graphen:",
sensebox_phyphox_createGraph: "Erstelle Graph",
sensebox_phyphox_graphLabel: "",
sensebox_phyphox_unitx: "Einheit x-Achse",
sensebox_phyphox_unity: "Einheit y-Achse",
sensebox_phyphox_labelx: "Beschriftung x-Achse",
sensebox_phyphox_labely: "Beschriftung y-Achse",
sensebox_phyphox_channel0: "Wert x-Achse",
sensebox_phyphox_channel1: "Wert y-Achse",
sensebox_phyphox_style_dots: "Punkte",
sensebox_phyphox_style_line: "Linie",
sensebox_phyphox_timestamp: "Zeitstempel",
sensebox_phyphox_channel: "Kanal",
sensebox_phyphox_sendchannel: "sende an Kanal:",
sensebox_phyphox_graphStyle: "Stil",
sensebox_phyphox_init_tooltip:
"Initialisere das Bluetooth Bee. Stecke diese auf dem Steckplatz **XBEE1**. Gib dem Phphox Messgerät einen eindeutigen Namen, damit du dieses in der App wiederfindest",
sensebox_phyphox_experiment_tooltip:
"Erstelle ein Experiment und vergib einen eindeutigen Namen und eine kurze Beschreibung. Füge bis zu 5 verschiedene Graphen in der Ansicht hinzu. ",
sensebox_phyphox_graph_tooltip:
"Erstellt einen neuen Graph für das Experiment. Gibt die Einheit und Beschriftung für die Achsen an und wähle den Stil der Visualisuerng der Messwerte. Füge an die Schnittstellen für die Werte der X- und Y-Achse den Kanal an auf dem die Messwerte später gesendet werden. Möchtest du einen Zeitstempel über die Phyphox App erstellen lassen verbinde den Block *Zeitstempel*",
sensebox_phyphox_timestamp_tooltip:
"Verwende diesen Block, um einen Zeitstempel über die Phyphox App erstellen zu lassen",
sensebox_phyphox_sendchannel_tooltip:
"Sendet einen Messwert an den ausgewählten Kanal",
sensebox_phyphox_experiment_send_tooltip:
"Sendet die Messwerte an die Phyphox App",
};

View File

@ -1,36 +1,40 @@
export const LED = { export const LED = {
/** /**
* WS2818 RGB LED * WS2818 RGB LED
*/ */
senseBox_ws2818_rgb_led: "Setze RGB-LED an", senseBox_ws2818_rgb_led: "Setze RGB-LED an",
senseBox_ws2818_rgb_led_init: "RGB LED (WS2818) initialisieren", senseBox_ws2818_rgb_led_init: "RGB LED (WS2818) initialisieren",
senseBox_ws2818_rgb_led_position: "Position", senseBox_ws2818_rgb_led_position: "Position",
senseBox_ws2818_rgb_led_brightness: "Helligkeit", senseBox_ws2818_rgb_led_brightness: "Helligkeit",
senseBox_ws2818_rgb_led_tooltip: "Verändere mit diesem Block die Farbe deiner RGB-LED. Verbinde einen Block für die Farbe. Wenn mehrere RGB-LEDs miteinander verkettet werden kannst du über die Position bestimmen welche LED angesteuert wird. ", senseBox_ws2818_rgb_led_tooltip:
senseBox_ws2818_rgb_led_init_tooltip: "Schließe die RGB-LED an einen der drei **digital/analog Ports** an. Wenn mehrere RGB-LEDs miteinander verkettet werden kannst du über die Position bestimmen welche LED angesteuert wird. ", "Verändere mit diesem Block die Farbe deiner RGB-LED. Verbinde einen Block für die Farbe. Wenn mehrere RGB-LEDs miteinander verkettet werden kannst du über die Position bestimmen welche LED angesteuert wird. ",
senseBox_ws2818_rgb_led_color: "Farbe", senseBox_ws2818_rgb_led_init_tooltip:
senseBox_ws2818_rgb_led_number: "Anzahl", "Schließe die RGB-LED an einen der drei **digital/analog Ports** an. Wenn mehrere RGB-LEDs miteinander verkettet werden kannst du über die Position bestimmen welche LED angesteuert wird. ",
senseBox_ws2818_rgb_led_color: "Farbe",
senseBox_ws2818_rgb_led_number: "Anzahl",
/** /**
* Color * Color
*/ */
COLOUR_BLEND_COLOUR1: "Farbe 1", COLOUR_BLEND_COLOUR1: "Farbe 1",
COLOUR_BLEND_COLOUR2: "mit Farbe 2", COLOUR_BLEND_COLOUR2: "mit Farbe 2",
COLOUR_BLEND_HELPURL: "http://meyerweb.com/eric/tools/color-blend/", COLOUR_BLEND_HELPURL: "http://meyerweb.com/eric/tools/color-blend/",
COLOUR_BLEND_RATIO: "im Verhältnis", COLOUR_BLEND_RATIO: "im Verhältnis",
COLOUR_BLEND_TITLE: "mische", COLOUR_BLEND_TITLE: "mische",
COLOUR_BLEND_TOOLTIP: "Vermische 2 Farben mit konfigurierbaren Farbverhältnis (0.0 - 1.0).", COLOUR_BLEND_TOOLTIP:
COLOUR_PICKER_HELPURL: "https://de.wikipedia.org/wiki/Farbe", "Vermische 2 Farben mit konfigurierbaren Farbverhältnis (0.0 - 1.0).",
COLOUR_PICKER_TOOLTIP: "Wähle eine Farbe aus der Palette. Die Farbe wird automatisch in RGB-Werte konvertiert.", COLOUR_PICKER_HELPURL: "https://de.wikipedia.org/wiki/Farbe",
COLOUR_RANDOM_HELPURL: "http://randomcolour.com", // untranslated COLOUR_PICKER_TOOLTIP:
COLOUR_RANDOM_TITLE: "zufällige Farbe", "Wähle eine Farbe aus der Palette. Die Farbe wird automatisch in RGB-Werte konvertiert.",
COLOUR_RANDOM_TOOLTIP: "Erstelle eine Farbe nach dem Zufallsprinzip.", COLOUR_RANDOM_HELPURL: "http://randomcolour.com", // untranslated
COLOUR_RGB_BLUE: "blau", COLOUR_RANDOM_TITLE: "zufällige Farbe",
COLOUR_RGB_GREEN: "grün", COLOUR_RANDOM_TOOLTIP: "Erstelle eine Farbe nach dem Zufallsprinzip.",
COLOUR_RGB_HELPURL: "https://de.wikipedia.org/wiki/RGB-Farbraum", COLOUR_RGB_BLUE: "blau",
COLOUR_RGB_RED: "rot", COLOUR_RGB_GREEN: "grün",
COLOUR_RGB_TITLE: "Farbe mit", COLOUR_RGB_HELPURL: "https://de.wikipedia.org/wiki/RGB-Farbraum",
COLOUR_RGB_TOOLTIP: "Erstelle eine Farbe mit selbst definierten Rot-, Grün- und Blauwerten. Alle Werte müssen zwischen 0 und 255 liegen. 0 ist hierbei die geringte Intensität der Farbe 255 die höchste.", COLOUR_RGB_RED: "rot",
COLOUR_RGB_TITLE: "Farbe mit",
} COLOUR_RGB_TOOLTIP:
"Erstelle eine Farbe mit selbst definierten Rot-, Grün- und Blauwerten. Alle Werte müssen zwischen 0 und 255 liegen. 0 ist hierbei die geringte Intensität der Farbe 255 die höchste.",
};

View File

@ -1,4 +1,5 @@
import { AUDIO } from "./en/audio"; import { AUDIO } from "./en/audio";
import { BLE } from "./en/sensebox-ble";
import { FAQ } from "./en/faq"; import { FAQ } from "./en/faq";
import { IO } from "./en/io"; import { IO } from "./en/io";
import { LOGIC } from "./en/logic"; import { LOGIC } from "./en/logic";
@ -24,6 +25,7 @@ import { WEBSERVER } from "./en/webserver";
export const En = { export const En = {
...AUDIO, ...AUDIO,
...BLE,
...FAQ, ...FAQ,
...IO, ...IO,
...LOGIC, ...LOGIC,

View File

@ -0,0 +1,42 @@
export const BLE = {
/**
* Phyphox Init
*/
sensebox_phyphox_init: "Initialise Phyphox device with name:",
sensebox_phyphox_createExperiment: "Create experiment",
sensebox_phyphox_experimentName: "Name of experiment",
sensebox_phyphox_experimentTitle: "Title",
sensebox_phyphox_experimentCategory: "Category",
sensebox_phyphox_experimentDescription: "Description",
sensebox_phyphox_experiment_description:
"Short description of the experiment",
sensebox_phyphox_writeValues: "Send values",
sensebox_phyphox_createView: "With graphs:",
sensebox_phyphox_createGraph: "Create Graph",
sensebox_phyphox_graphLabel: "",
sensebox_phyphox_unitx: "Unit x-axis",
sensebox_phyphox_unity: "Unit y-axis",
sensebox_phyphox_labelx: "Label x-axis",
sensebox_phyphox_labely: "Label y-axis",
sensebox_phyphox_channel0: "x-axis value",
sensebox_phyphox_channel1: "y-axis value",
sensebox_phyphox_style_dots: "Dots",
sensebox_phyphox_style_line: "Line",
sensebox_phyphox_timestamp: "Timestamp",
sensebox_phyphox_channel: "Channel",
sensebox_phyphox_sendchannel: "send to channel:",
sensebox_phyphox_graphStyle: "style",
sensebox_phyphox_init_tooltip:
"Initialise the Bluetooth Bee. Plug it into the **XBEE1** slot. Give the Phphox meter a unique name so you can find it in the app",
sensebox_phyphox_experiment_tooltip:
"Create an experiment and give it a unique name and a short description. Add up to 5 different graphs in the view. ",
sensebox_phyphox_graph_tooltip:
"Creates a new graph for the experiment. Specify the unit and label for the axes and choose the style of visualisation of the measured values. Add to the interfaces for the values of the X- and Y-axis the channel on which the measured values will be sent later. If you want to create a timestamp via the Phyphox app, connect the block *Timestamp*",
sensebox_phyphox_timestamp_tooltip:
"Use this block to have a timestamp created via the Phyphox app",
sensebox_phyphox_sendchannel_tooltip:
"Sends a reading to the selected channel",
sensebox_phyphox_experiment_send_tooltip:
"Sends the measured values to the Phyphox App",
};

View File

@ -347,6 +347,30 @@ class Toolbox extends React.Component {
<Block type="sensebox_lora_cayenne_gps" /> <Block type="sensebox_lora_cayenne_gps" />
</Category> </Category>
</Category> </Category>
<Category id="phyphox" name="Phyphox" colour={getColour().phyphox}>
<Block type="sensebox_phyphox_init"></Block>
<Block type="sensebox_phyphox_experiment">
<Value name="view">
<Block type="sensebox_phyphox_graph">
<Value name="channel0">
<Block type="sensebox_phyphox_timestamp"></Block>
</Value>
<Value name="channel1">
<Block type="sensebox_phyphox_channel"></Block>
</Value>
</Block>
</Value>
</Block>
<Block type="sensebox_phyphox_experiment_send">
<Value name="sendValues">
<Block type="sensebox_phyphox_sendchannel"></Block>
</Value>
</Block>
<Block type="sensebox_phyphox_graph"></Block>
<Block type="sensebox_phyphox_timestamp"></Block>
<Block type="sensebox_phyphox_channel"></Block>
<Block type="sensebox_phyphox_sendchannel"></Block>
</Category>
<Category <Category
id="webserver" id="webserver"
name="Webserver" name="Webserver"