OMRONオムロンモジュール

本ページには広告が含まれています。

環境センサーからセンサー値を取得します。

構文
OMRON.DeviceInformation( UUID, cache )
OMRON.LatestSensingData( field, cache )
OMRON.LatestCalculationData( field, cache )
OMRON.LatestDataLong( type, data )
引数
UUID 必須
取得する項目のUUID
field 必須
取得する項目のフィールド
type 必須
取得する項目のタイプ
cache 省略可
キャッシュから取得するかを示すブール値
戻り値

プログラム

UWSC
//////////////////////////////////////////////////
// 【引数】
//   UUID : 取得する項目のUUID
//   field : 取得する項目のフィールド
//   type : 取得する項目のタイプ
//   cache : キャッシュから取得するかを示すブール値
// 【戻り値】
//////////////////////////////////////////////////
CONST OMRON_GET_DATA = -1
CONST OMRON_CREATION_TIME = -2
CONST OMRON_CACHE_EXISTS = -3
CONST OMRON_CACHE_CLEAR = -4

// 0x180A
CONST DEVICE_INFORMATION_MODEL_NUMBER = $2A24
CONST DEVICE_INFORMATION_SERIAL_NUMBER = $2A25
CONST DEVICE_INFORMATION_FIRMWARE_REVISION = $2A26
CONST DEVICE_INFORMATION_HARDWARE_REVISION = $2A27
CONST DEVICE_INFORMATION_MANUFACTURE_NAME = $2A29

// 0x5012
CONST LATEST_SENSING_DATA_SEQUENCE_NUMBER = 0
CONST LATEST_SENSING_DATA_TEMPERATURE = 1
CONST LATEST_SENSING_DATA_RELATIVE_HUMIDITY = 2
CONST LATEST_SENSING_DATA_AMBIENT_LIGHT = 3
CONST LATEST_SENSING_DATA_BAROMETRIC_PRESSURE = 4
CONST LATEST_SENSING_DATA_SOUND_NOISE = 5
CONST LATEST_SENSING_DATA_ETVOC = 6
CONST LATEST_SENSING_DATA_ECO2 = 7

// 0x5013
CONST LATEST_CALCULATION_DATA_SEQUENCE_NUMBER = 0
CONST LATEST_CALCULATION_DATA_DISCOMFORT_INDEX = 1
CONST LATEST_CALCULATION_DATA_HEAT_STROKE = 2
CONST LATEST_CALCULATION_DATA_VIBRATION_INFORMATION = 3
CONST LATEST_CALCULATION_DATA_SI_VALUE = 4
CONST LATEST_CALCULATION_DATA_PGA = 5
CONST LATEST_CALCULATION_DATA_SEISMIC_INTENSITY = 6
CONST LATEST_CALCULATION_DATA_ACCELERATION_X_AXIS = 7
CONST LATEST_CALCULATION_DATA_ACCELERATION_Y_AXIS = 8
CONST LATEST_CALCULATION_DATA_ACCELERATION_Z_AXIS = 9

// 0x5111
CONST DISPLAY_RULE_NORMALLY_OFF = 0
CONST DISPLAY_RULE_NORMALLY_ON = 1
CONST DISPLAY_RULE_TEMPERATURE_VALUE_SCALES = 2
CONST DISPLAY_RULE_RELATIVE_HUMIDITY_VALUE_SCALES = 3
CONST DISPLAY_RULE_AMBIENT_LIGHT_VALUE_SCALES = 4
CONST DISPLAY_RULE_BAROMETRIC_PRESSURE_VALUE_SCALES = 5
CONST DISPLAY_RULE_SOUND_NOISE_VALUE_SCALES = 6
CONST DISPLAY_RULE_ETVOC_VALUE_SCALES = 7
CONST DISPLAY_RULE_SI_VALUE_SCALES = 8
CONST DISPLAY_RULE_PGA_VALUE_SCALES = 9

// 0x0521
CONST LATEST_DATA_LONG_SEQUENCE_NUMBER = 0
CONST LATEST_DATA_LONG_TEMPERATURE = 1				// 温度
CONST LATEST_DATA_LONG_RELATIVE_HUMIDITY = 2			// 相対湿度
CONST LATEST_DATA_LONG_AMBIENT_LIGHT = 3				// 照度
CONST LATEST_DATA_LONG_BAROMETRIC_PRESSURE = 4			// 気圧
CONST LATEST_DATA_LONG_SOUND_NOISE = 5				// 騒音
CONST LATEST_DATA_LONG_ETVOC = 6					// eTVOC(総揮発性有機化合物濃度)
CONST LATEST_DATA_LONG_ECO2 = 7					// CO2濃度相当値
CONST LATEST_DATA_LONG_DISCOMFORT_INDEX = 8			// 不快指数
CONST LATEST_DATA_LONG_HEAT_STROKE = 9				// 熱中症警戒度
CONST LATEST_DATA_LONG_VIBRATION_INFORMATION = 10		// 振動情報
CONST LATEST_DATA_LONG_SI_VALUE = 11				// スペクトル強度
CONST LATEST_DATA_LONG_PGA = 12					// 最大加速度値
CONST LATEST_DATA_LONG_SEISMIC_INTENSITY = 13			// 計測震度相当値
CONST LATEST_DATA_LONG_TEMPERATURE_FLAG = 14			// 温度フラグ
CONST LATEST_DATA_LONG_RELATIVE_HUMIDITY_FLAG = 15		// 相対湿度フラグ
CONST LATEST_DATA_LONG_AMBIENT_LIGHT_FLAG = 16			// 照度フラグ
CONST LATEST_DATA_LONG_BAROMETRIC_PRESSURE_FLAG = 17		// 気圧フラグ
CONST LATEST_DATA_LONG_SOUND_NOISE_FLAG = 18			// 騒音フラグ
CONST LATEST_DATA_LONG_ETVOC_FLAG = 19				// 
CONST LATEST_DATA_LONG_ECO2_FLAG = 20				// 
CONST LATEST_DATA_LONG_DISCOMFORT_INDEX_FLAG = 21		// 不快指数フラグ
CONST LATEST_DATA_LONG_HEAT_STROKE_FLAG = 22			// 熱中症警戒度フラグ
CONST LATEST_DATA_LONG_SI_VALUE_FLAG = 23				// スペクトル強度フラグ
CONST LATEST_DATA_LONG_PGA_FLAG = 24				// 最大加速度値フラグ
CONST LATEST_DATA_LONG_SEISMIC_INTENSITY_FLAG = 25		// 計測震度相当値フラグ

MODULE OMRON
	CONST ssfBITBUCKET = 10
	DIM filename = "2JCIE-BU01.py"
	DIM Shell = CREATEOLEOBJ("Shell.Application")
	DIM FSO = CREATEOLEOBJ("Scripting.FileSystemObject")
	PROCEDURE OMRON()
	FEND
	FUNCTION DeviceInformation(UUID, cache = FALSE)
		jsonname = "180A.json"
		IFB UUID = OMRON_GET_DATA THEN
			cmd = "python " + filename + " " + FSO.GetBaseName(jsonname) + " <#DBL>" + UUID + "<#DBL>"
			RESULT = DOSCMD(cmd)
		ELSEIF UUID = OMRON_CREATION_TIME THEN
			IFB OMRON.DeviceInformation(OMRON_CACHE_EXISTS) THEN
				source = FSO.BuildPath(GET_CUR_DIR, jsonname)
				DIM FID = FOPEN(source, F_READ)	
				DIM text = FGET(FID, F_ALLTEXT)
				FCLOSE(FID)
				DIM obj = JSON.Parse(text)
				RESULT = obj.DateTime
			ELSE
				RESULT = ERR_VALUE
			ENDIF
		ELSEIF UUID = OMRON_CACHE_EXISTS THEN
			RESULT = FSO.FileExists(FSO.BuildPath(GET_CUR_DIR, jsonname))
		ELSEIF UUID = OMRON_CACHE_CLEAR THEN
			DIM Folder = Shell.NameSpace(ssfBITBUCKET)
			source = FSO.BuildPath(GET_CUR_DIR, jsonname)
			Folder.MoveHere(source)
		ELSEIF UUID >= 0 THEN
			HASHTBL Contents
			Contents[10788] = "ModelNumber"
			Contents[10789] = "SerialNumber"
			Contents[10790] = "FirmwareRevision"
			Contents[10791] = "HardwareRevision"
			Contents[10793] = "ManufacturerName"
			FID = FOPEN(jsonname, F_READ)
			DIM json = FGET(FID, F_ALLTEXT)
			obj = JSON.parse(json)
			RESULT = EVAL("obj." + Contents[UUID])
		ENDIF
	FEND
	FUNCTION LatestSensingData(field, cache = FALSE)
		jsonname = "5012.json"
		IFB field = OMRON_GET_DATA THEN
			RESULT = DOSCMD("python " + filename + " " + FSO.GetBaseName(jsonname) + " <#DBL>" + field + "<#DBL>")
		ELSEIF field = OMRON_CREATION_TIME THEN
			IFB OMRON.LatestSensingData(OMRON_CACHE_EXISTS) THEN
				source = FSO.BuildPath(GET_CUR_DIR, jsonname)
				DIM FID = FOPEN(source, F_READ)	
				DIM text = FGET(FID, F_ALLTEXT)
				FCLOSE(FID)
				DIM obj = JSON.Parse(text)
				RESULT = obj.DateTime
			ELSE
				RESULT = ERR_VALUE
			ENDIF
		ELSEIF field = OMRON_CACHE_EXISTS THEN
			RESULT = FSO.FileExists(FSO.BuildPath(GET_CUR_DIR, jsonname))
		ELSEIF field = OMRON_CACHE_CLEAR THEN
			DIM Folder = Shell.NameSpace(ssfBITBUCKET)
			source = FSO.BuildPath(GET_CUR_DIR, jsonname)
			Folder.MoveHere(source)
		ELSE
			DIM Contents[] = "SequenceNumber","Temperature", "RelativeHumidity", "AmbientLight", "BarometricPressure", "SoundNoise", "eTVOC", "eCO2"
			FID = FOPEN(jsonname, F_READ)
			json = FGET(FID, F_ALLTEXT)
			obj = JSON.parse(json)
			RESULT = EVAL("obj." + Contents[field])
		ENDIF
	FEND
	FUNCTION LatestCalculationData(field, cache = FALSE)
		jsonname = "5013.json"
		IFB field = OMRON_GET_DATA THEN
			RESULT = DOSCMD("python " + filename + " " + FSO.GetBaseName(jsonname) + " <#DBL>" + field  + "<#DBL>")
		ELSEIF field = OMRON_CREATION_TIME THEN
			IFB OMRON.LatestCalculationData(OMRON_CACHE_EXISTS) THEN
				source = FSO.BuildPath(GET_CUR_DIR, jsonname)
				DIM FID = FOPEN(source, F_READ)	
				DIM text = FGET(FID, F_ALLTEXT)
				FCLOSE(FID)
				DIM obj = JSON.Parse(text)
				RESULT = obj.DateTime
			ELSE
				RESULT = ERR_VALUE
			ENDIF
		ELSEIF field = OMRON_CACHE_EXISTS THEN
			RESULT = FSO.FileExists(FSO.BuildPath(GET_CUR_DIR, jsonname))
		ELSEIF field = OMRON_CACHE_CLEAR THEN
			DIM Folder = Shell.NameSpace(ssfBITBUCKET)
			source = FSO.BuildPath(GET_CUR_DIR, jsonname)
			Folder.MoveHere(source)
		ELSE
			DIM Contents[] = "SequenceNumber", "DiscomfortIndex", "HeatStroke", "VibrationInformation", "SIValue", "PGA", "SeismicIntensity", "AccelerationXAxis", "AccelerationYAxis", "AccelerationZAxis"
			FID = FOPEN(jsonname, F_READ)
			json = FGET(FID, F_ALLTEXT)
			obj = JSON.parse(json)
			RESULT = EVAL("obj." + Contents[field])
		ENDIF
	FEND
	FUNCTION LatestDataLong(type, data = "")
		jsonname = "5021.json"
		IFB type = OMRON_GET_DATA THEN
			RESULT = DOSCMD("python " + filename + " " + FSO.GetBaseName(jsonname) + " <#DBL>" + type + "<#DBL>")
		ELSEIF type = OMRON_CREATION_TIME THEN
			IFB OMRON.LatestDataLong(OMRON_CACHE_EXISTS) THEN
				source = FSO.BuildPath(GET_CUR_DIR, jsonname)
				DIM FID = FOPEN(source, F_READ)
				DIM text = FGET(FID, F_ALLTEXT)
				FCLOSE(FID)
				DIM obj = JSON.Parse(text)
				RESULT = obj.DateTime
			ELSE
				RESULT = ERR_VALUE
			ENDIF
		ELSEIF type = OMRON_CACHE_EXISTS THEN
			RESULT = FSO.FileExists(FSO.BuildPath(GET_CUR_DIR, jsonname))			
		ELSEIF type = OMRON_CACHE_CLEAR THEN
			DIM Folder = Shell.NameSpace(ssfBITBUCKET)
			source = FSO.BuildPath(GET_CUR_DIR, jsonname)
			Folder.MoveHere(source)			
		ELSE
			DIM Contents[] = "SequenceNumber", "Temperature", "RelativeHumidity", "AmbientLight", "BarometricPressure", "SoundNoise", "eTVOC", "eCO2", "DiscomfortIndex", "HeatStroke", "VibrationInformation", "SIValue", "PGA", "SeismicIntensity", "TemperatureFlag", "RelativeHumidityFlag", "AmbientLightFlag", "BarometricPressureFlag", "SoundNoiseFlag", "eTVOCFlag", "eCO2Flag", "DiscomfortIndexFlag", "HeatStrokeFlag", "SIValueFlag", "PGAFlag", "SeismicIntensityFlag"
			FID = FOPEN(jsonname, F_READ)
			DIM json = FGET(FID, F_ALLTEXT)
			obj = JSON.parse(json)
			RESULT = EVAL("obj." + Contents[type])
		ENDIF
	FEND
	FUNCTION LEDSetting(normalState, red, green, blue)
		jsonname = "5111.json"
		RESULT = DOSCMD("python " + filename + " " + FSO.GetBaseName(jsonname) + " " + normalState + " " + red + " " + green + " " + blue)
	FEND
	FUNCTION MountingOrientation()
		jsonname = "5402.json"
		RESULT = DOSCMD("python " + filename + " " + FSO.GetBaseName(jsonname) + "")
	FEND
ENDMODULE

//////////////////////////////////////////////////
// 【引数】
//   text : JSONとして解析する文字列
//   value : JSON文字列に変換する値
//   reviver : 使用不可
//   replacer : 使用不可
//   space : 出力するJSON文字列に空白を挿入するための文字列もしくは数値
// 【戻り値】
//    : Parse : JSON文字列をオブジェクトに変換、
//   Stringify : オブジェクトをJSON文字列に変換、 :
//////////////////////////////////////////////////
MODULE JSON
	DIM SC, CodeObject
	PROCEDURE JSON
		SC = CREATEOLEOBJ("ScriptControl")
		WITH SC
			.Language = "JScript"
			.ExecuteStatement(json2)
			.ExecuteStatement(statement)
			CodeObject = .CodeObject
		ENDWITH
	FEND
	FUNCTION Parse(text, reviver = NULL)
		RESULT = CodeObject.JSON.parse(text, reviver)
	FEND
	FUNCTION Stringify(value, replacer = "", space = FALSE)
		RESULT = CodeObject.JSON.stringify(value, NULL, replacer)
		IF space THEN RESULT = REPLACE(RESULT, CHR(10), "<#CR>")
	FEND
ENDMODULE

TEXTBLOCK statement
Array.prototype.Item = function(i, value){
	if(value === undefined) return this[i]; this[i] = value;
}
Array.prototype.item = Array.prototype.Item;
ENDTEXTBLOCK

TEXTBLOCK json2
//  json2.js
//  2023-05-10
//  Public Domain.
//  NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.

//  USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
//  NOT CONTROL.

//  This file creates a global JSON object containing two methods: stringify
//  and parse. This file provides the ES5 JSON capability to ES3 systems.
//  If a project might run on IE8 or earlier, then this file should be included.
//  This file does nothing on ES5 systems.

//      JSON.stringify(value, replacer, space)
//          value       any JavaScript value, usually an object or array.
//          replacer    an optional parameter that determines how object
//                      values are stringified for objects. It can be a
//                      function or an array of strings.
//          space       an optional parameter that specifies the indentation
//                      of nested structures. If it is omitted, the text will
//                      be packed without extra whitespace. If it is a number,
//                      it will specify the number of spaces to indent at each
//                      level. If it is a string (such as "\t" or " "),
//                      it contains the characters used to indent at each level.
//          This method produces a JSON text from a JavaScript value.
//          When an object value is found, if the object contains a toJSON
//          method, its toJSON method will be called and the result will be
//          stringified. A toJSON method does not serialize: it returns the
//          value represented by the name/value pair that should be serialized,
//          or undefined if nothing should be serialized. The toJSON method
//          will be passed the key associated with the value, and this will be
//          bound to the value.

//          For example, this would serialize Dates as ISO strings.

//              Date.prototype.toJSON = function (key) {
//                  function f(n) {
//                      // Format integers to have at least two digits.
//                      return (n < 10)
//                          ? "0" + n
//                          : n;
//                  }
//                  return this.getUTCFullYear()   + "-" +
//                       f(this.getUTCMonth() + 1) + "-" +
//                       f(this.getUTCDate())      + "T" +
//                       f(this.getUTCHours())     + ":" +
//                       f(this.getUTCMinutes())   + ":" +
//                       f(this.getUTCSeconds())   + "Z";
//              };

//          You can provide an optional replacer method. It will be passed the
//          key and value of each member, with this bound to the containing
//          object. The value that is returned from your method will be
//          serialized. If your method returns undefined, then the member will
//          be excluded from the serialization.

//          If the replacer parameter is an array of strings, then it will be
//          used to select the members to be serialized. It filters the results
//          such that only members with keys listed in the replacer array are
//          stringified.

//          Values that do not have JSON representations, such as undefined or
//          functions, will not be serialized. Such values in objects will be
//          dropped; in arrays they will be replaced with null. You can use
//          a replacer function to replace those with JSON values.

//          JSON.stringify(undefined) returns undefined.

//          The optional space parameter produces a stringification of the
//          value that is filled with line breaks and indentation to make it
//          easier to read.

//          If the space parameter is a non-empty string, then that string will
//          be used for indentation. If the space parameter is a number, then
//          the indentation will be that many spaces.

//          Example:

//          text = JSON.stringify(["e", {pluribus: "unum"}]);
//          // text is '["e",{"pluribus":"unum"}]'

//          text = JSON.stringify(["e", {pluribus: "unum"}], null, "\t");
//          // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'

//          text = JSON.stringify([new Date()], function (key, value) {
//              return this[key] instanceof Date
//                  ? "Date(" + this[key] + ")"
//                  : value;
//          });
//          // text is '["Date(---current time---)"]'

//      JSON.parse(text, reviver)
//          This method parses a JSON text to produce an object or array.
//          It can throw a SyntaxError exception.

//          The optional reviver parameter is a function that can filter and
//          transform the results. It receives each of the keys and values,
//          and its return value is used instead of the original value.
//          If it returns what it received, then the structure is not modified.
//          If it returns undefined then the member is deleted.

//          Example:

//          // Parse the text. Values that look like ISO date strings will
//          // be converted to Date objects.

//          myData = JSON.parse(text, function (key, value) {
//              var a;
//              if (typeof value === "string") {
//                  a =
//   /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
//                  if (a) {
//                      return new Date(Date.UTC(
//                         +a[1], +a[2] - 1, +a[3], +a[4], +a[5], +a[6]
//                      ));
//                  }
//                  return value;
//              }
//          });

//          myData = JSON.parse(
//              "[\"Date(09/09/2001)\"]",
//              function (key, value) {
//                  var d;
//                  if (
//                      typeof value === "string"
//                      && value.slice(0, 5) === "Date("
//                      && value.slice(-1) === ")"
//                  ) {
//                      d = new Date(value.slice(5, -1));
//                      if (d) {
//                          return d;
//                      }
//                  }
//                  return value;
//              }
//          );

//  This is a reference implementation. You are free to copy, modify, or
//  redistribute.

/*jslint
    eval, for, this
*/

/*property
    JSON, apply, call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
    getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
    lastIndex, length, parse, prototype, push, replace, slice, stringify,
    test, toJSON, toString, valueOf
*/


// Create a JSON object only if one does not already exist. We create the
// methods in a closure to avoid creating global variables.

if (typeof JSON !== "object") {
    JSON = {};
}

(function () {
    "use strict";

    var rx_one = /^[\],:{}\s]*$/;
    var rx_two = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g;
    var rx_three = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g;
    var rx_four = /(?:^|:|,)(?:\s*\[)+/g;
    var rx_escapable = /[\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
    var rx_dangerous = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;

    function f(n) {
        // Format integers to have at least two digits.
        return (n < 10)
            ? "0" + n
            : n;
    }

    function this_value() {
        return this.valueOf();
    }

    if (typeof Date.prototype.toJSON !== "function") {

        Date.prototype.toJSON = function () {

            return isFinite(this.valueOf())
                ? (
                    this.getUTCFullYear()
                    + "-"
                    + f(this.getUTCMonth() + 1)
                    + "-"
                    + f(this.getUTCDate())
                    + "T"
                    + f(this.getUTCHours())
                    + ":"
                    + f(this.getUTCMinutes())
                    + ":"
                    + f(this.getUTCSeconds())
                    + "Z"
                )
                : null;
        };

        Boolean.prototype.toJSON = this_value;
        Number.prototype.toJSON = this_value;
        String.prototype.toJSON = this_value;
    }

    var gap;
    var indent;
    var meta;
    var rep;


    function quote(string) {

// If the string contains no control characters, no quote characters, and no
// backslash characters, then we can safely slap some quotes around it.
// Otherwise we must also replace the offending characters with safe escape
// sequences.

        rx_escapable.lastIndex = 0;
        return rx_escapable.test(string)
            ? "\"" + string.replace(rx_escapable, function (a) {
                var c = meta[a];
                return typeof c === "string"
                    ? c
                    : "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4);
            }) + "\""
            : "\"" + string + "\"";
    }


    function str(key, holder) {

// Produce a string from holder[key].

        var i;          // The loop counter.
        var k;          // The member key.
        var v;          // The member value.
        var length;
        var mind = gap;
        var partial;
        var value = holder[key];

// If the value has a toJSON method, call it to obtain a replacement value.

        if (
            value
            && typeof value === "object"
            && typeof value.toJSON === "function"
        ) {
            value = value.toJSON(key);
        }

// If we were called with a replacer function, then call the replacer to
// obtain a replacement value.

        if (typeof rep === "function") {
            value = rep.call(holder, key, value);
        }

// What happens next depends on the value's type.

        switch (typeof value) {
        case "string":
            return quote(value);

        case "number":

// JSON numbers must be finite. Encode non-finite numbers as null.

            return (isFinite(value))
                ? String(value)
                : "null";

        case "boolean":
        case "null":

// If the value is a boolean or null, convert it to a string. Note:
// typeof null does not produce "null". The case is included here in
// the remote chance that this gets fixed someday.

            return String(value);

// If the type is "object", we might be dealing with an object or an array or
// null.

        case "object":

// Due to a specification blunder in ECMAScript, typeof null is "object",
// so watch out for that case.

            if (!value) {
                return "null";
            }

// Make an array to hold the partial results of stringifying this object value.

            gap += indent;
            partial = [];

// Is the value an array?

            if (Object.prototype.toString.apply(value) === "[object Array]") {

// The value is an array. Stringify every element. Use null as a placeholder
// for non-JSON values.

                length = value.length;
                for (i = 0; i < length; i += 1) {
                    partial[i] = str(i, value) || "null";
                }

// Join all of the elements together, separated with commas, and wrap them in
// brackets.

                v = partial.length === 0
                    ? "[]"
                    : gap
                        ? (
                            "[\n"
                            + gap
                            + partial.join(",\n" + gap)
                            + "\n"
                            + mind
                            + "]"
                        )
                        : "[" + partial.join(",") + "]";
                gap = mind;
                return v;
            }

// If the replacer is an array, use it to select the members to be stringified.

            if (rep && typeof rep === "object") {
                length = rep.length;
                for (i = 0; i < length; i += 1) {
                    if (typeof rep[i] === "string") {
                        k = rep[i];
                        v = str(k, value);
                        if (v) {
                            partial.push(quote(k) + (
                                (gap)
                                    ? ": "
                                    : ":"
                            ) + v);
                        }
                    }
                }
            } else {

// Otherwise, iterate through all of the keys in the object.

                for (k in value) {
                    if (Object.prototype.hasOwnProperty.call(value, k)) {
                        v = str(k, value);
                        if (v) {
                            partial.push(quote(k) + (
                                (gap)
                                    ? ": "
                                    : ":"
                            ) + v);
                        }
                    }
                }
            }

// Join all of the member texts together, separated with commas,
// and wrap them in braces.

            v = partial.length === 0
                ? "{}"
                : gap
                    ? "{\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "}"
                    : "{" + partial.join(",") + "}";
            gap = mind;
            return v;
        }
    }

// If the JSON object does not yet have a stringify method, give it one.

    if (typeof JSON.stringify !== "function") {
        meta = {    // table of character substitutions
            "\b": "\\b",
            "\t": "\\t",
            "\n": "\\n",
            "\f": "\\f",
            "\r": "\\r",
            "\"": "\\\"",
            "\\": "\\\\"
        };
        JSON.stringify = function (value, replacer, space) {

// The stringify method takes a value and an optional replacer, and an optional
// space parameter, and returns a JSON text. The replacer can be a function
// that can replace values, or an array of strings that will select the keys.
// A default replacer method can be provided. Use of the space parameter can
// produce text that is more easily readable.

            var i;
            gap = "";
            indent = "";

// If the space parameter is a number, make an indent string containing that
// many spaces.

            if (typeof space === "number") {
                for (i = 0; i < space; i += 1) {
                    indent += " ";
                }

// If the space parameter is a string, it will be used as the indent string.

            } else if (typeof space === "string") {
                indent = space;
            }

// If there is a replacer, it must be a function or an array.
// Otherwise, throw an error.

            rep = replacer;
            if (replacer && typeof replacer !== "function" && (
                typeof replacer !== "object"
                || typeof replacer.length !== "number"
            )) {
                throw new Error("JSON.stringify");
            }

// Make a fake root object containing our value under the key of "".
// Return the result of stringifying the value.

            return str("", {"": value});
        };
    }


// If the JSON object does not yet have a parse method, give it one.

    if (typeof JSON.parse !== "function") {
        JSON.parse = function (text, reviver) {

// The parse method takes a text and an optional reviver function, and returns
// a JavaScript value if the text is a valid JSON text.

            var j;

            function walk(holder, key) {

// The walk method is used to recursively walk the resulting structure so
// that modifications can be made.

                var k;
                var v;
                var value = holder[key];
                if (value && typeof value === "object") {
                    for (k in value) {
                        if (Object.prototype.hasOwnProperty.call(value, k)) {
                            v = walk(value, k);
                            if (v !== undefined) {
                                value[k] = v;
                            } else {
                                delete value[k];
                            }
                        }
                    }
                }
                return reviver.call(holder, key, value);
            }


// Parsing happens in four stages. In the first stage, we replace certain
// Unicode characters with escape sequences. JavaScript handles many characters
// incorrectly, either silently deleting them, or treating them as line endings.

            text = String(text);
            rx_dangerous.lastIndex = 0;
            if (rx_dangerous.test(text)) {
                text = text.replace(rx_dangerous, function (a) {
                    return (
                        "\\u"
                        + ("0000" + a.charCodeAt(0).toString(16)).slice(-4)
                    );
                });
            }

// In the second stage, we run the text against regular expressions that look
// for non-JSON patterns. We are especially concerned with "()" and "new"
// because they can cause invocation, and "=" because it can cause mutation.
// But just to be safe, we want to reject all unexpected forms.

// We split the second stage into 4 regexp operations in order to work around
// crippling inefficiencies in IE's and Safari's regexp engines. First we
// replace the JSON backslash pairs with "@" (a non-JSON character). Second, we
// replace all simple value tokens with "]" characters. Third, we delete all
// open brackets that follow a colon or comma or that begin the text. Finally,
// we look to see that the remaining characters are only whitespace or "]" or
// "," or ":" or "{" or "}". If that is so, then the text is safe for eval.

            if (
                rx_one.test(
                    text
                        .replace(rx_two, "@")
                        .replace(rx_three, "]")
                        .replace(rx_four, "")
                )
            ) {

// In the third stage we use the eval function to compile the text into a
// JavaScript structure. The "{" operator is subject to a syntactic ambiguity
// in JavaScript: it can begin a block or an object literal. We wrap the text
// in parens to eliminate the ambiguity.

                j = eval("(" + text + ")");

// In the optional fourth stage, we recursively walk the new structure, passing
// each name/value pair to a reviver function for possible transformation.

                return (typeof reviver === "function")
                    ? walk({"": j}, "")
                    : j;
            }

// If the text is not JSON parseable, then a SyntaxError is thrown.

            throw new SyntaxError("JSON.parse");
        };
    }
}());
ENDTEXTBLOCK

//////////////////////////////////////////////////
// 【引数】
//   serial : 時間を表すシリアル値を指定
// 【戻り値】
//////////////////////////////////////////////////
FUNCTION Second(serial)
	RESULT = REPLACE(FORMAT(INT(serial * 86400) MOD 60, 2), " ", "0")
FEND

Pythonプログラム

以下はPythonのプログラムで、OMRONモジュールと同じフォルダに2JCIE-BU01.pyというファイル名で保存します。

Python
import sys
import serial
import datetime
import time
from struct import pack, unpack
import chardet
import os
import json

OMRON_GET_DATA = -1
OMRON_CREATION_TIME = -2
OMRON_CACHE_EXISTS = -3
OMROM_CACHE_CLEAR = -4

# Device Information(0x180A)
DEVICE_INFORMATION_MODEL_NUMBER = 0x2A24
DEVICE_INFORMATION_SERIAL_NUMBER = 0x2A25
DEVICE_INFORMATION_FIRMWARE_REVISION = 0x2A26
DEVICE_INFORMATION_HARDWARE_REVISION = 0x2A27
DEVICE_INFORMATION_MANUFACTURE_NAME = 0x2A29

# Latest Sensing Data(0x5012)
LATEST_SENSING_DATA_SEQUENCE_NUMBER = 0
LATEST_SENSING_DATA_TEMPERATURE = 1
LATEST_SENSING_DATA_RELATIVE_HUMIDITY = 2
LATEST_SENSING_DATA_AMBIENT_LIGHT = 3
LATEST_SENSING_DATA_BAROMETRIC_PRESSURE = 4
LATEST_SENSING_DATA_SOUND_NOISE = 5
LATEST_SENSING_DATA_ETVOC = 6
LATEST_SENSING_DATA_ECO2 = 7

# Latest Calculation Data(0x5013)
LATEST_CALCULATION_DATA_SEQUENCE_NUMBER = 0
LATEST_CALCULATION_DATA_DISCOMFORT_INDEX = 1
LATEST_CALCULATION_DATA_HEAT_STROKE = 2
LATEST_CALCULATION_DATA_VIBRATION_INFORMATION = 3
LATEST_CALCULATION_DATA_SI_VALUE = 4
LATEST_CALCULATION_DATA_PGA = 5
LATEST_CALCULATION_DATA_SEISMIC_INTENSITY = 6
LATEST_CALCULATION_DATA_ACCELERATION_X_AXIS = 7
LATEST_CALCULATION_DATA_ACCELERATION_Y_AXIS = 8
LATEST_CALCULATION_DATA_ACCELERATION_Z_AXIS = 9

# Latest Data Long(0x5021)
LATEST_DATA_LONG_SEQUENCE_NUMBER = 0
LATEST_DATA_LONG_TEMPERATURE = 1
LATEST_DATA_LONG_RELATIVE_HUMIDITY = 2
LATEST_DATA_LONG_AMBIENT_LIGHT = 3
LATEST_DATA_LONG_BAROMETRIC_PRESSURE = 4
LATEST_DATA_LONG_SOUND_NOISE = 5
LATEST_DATA_LONG_ETVOC = 6
LATEST_DATA_LONG_ECO2 = 7
LATEST_DATA_LONG_DISCOMFORT_INDEX = 8
LATEST_DATA_LONG_HEAT_STROKE = 9
LATEST_DATA_LONG_VIBRATION_INFORMATION = 10
LATEST_DATA_LONG_SI_VALUE = 11
LATEST_DATA_LONG_PGA = 12
LATEST_DATA_LONG_SEISMIC_INTENSITY = 13
LATEST_DATA_LONG_TEMPERATURE_FLAG = 14
LATEST_DATA_LONG_RELATIVE_HUMIDITY_FLAG = 15
LATEST_DATA_LONG_AMBIENT_LIGHT_FLAG = 16
LATEST_DATA_LONG_BAROMETRIC_PRESSURE_FLAG = 17
LATEST_DATA_LONG_SOUND_NOISE_FLAG = 18
LATEST_DATA_LONG_ETVOC_FLAG = 19
LATEST_DATA_LONG_ECO2_FLAG = 20
LATEST_DATA_LONG_DISCOMFORT_INDEX_FLAG = 21
LATEST_DATA_LONG_HEAT_STROKE_FLAG = 22
LATEST_DATA_LONG_SI_VALUE_FLAG = 23
LATEST_DATA_LONG_PGA_FLAG = 24
LATEST_DATA_LONG_SEISMIC_INTENSITY_FLAG = 25

# LED setting [normal state](0x5111)
DISPLAY_RULE_NORMALLY_OFF = 0
DISPLAY_RULE_NORMALLY_ON = 1
DISPLAY_RULE_TEMPERATURE_VALUE_SCALES = 2
DISPLAY_RULE_RELATIVE_HUMIDITY_VALUE_SCALES = 3
DISPLAY_RULE_AMBIENT_LIGHT_VALUE_SCALES = 4
DISPLAY_RULE_BAROMETRIC_PRESSURE_VALUE_SCALES = 5
DISPLAY_RULE_SOUND_NOISE_VALUE_SCALES = 6
DISPLAY_RULE_ETVOC_VALUE_SCALES = 7
DISPLAY_RULE_SI_VALUE_SCALES = 8
DISPLAY_RULE_PGA_VALUE_SCALES = 9

# 0x0521
LATEST_DATA_LONG_SEQUENCE_NUMBER = 0
LATEST_DATA_LONG_TEMPERATURE = 1
LATEST_DATA_LONG_RELATIVE_HUMIDITY = 2
LATEST_DATA_LONG_AMBIENT_LIGHT = 3
LATEST_DATA_LONG_BAROMETRIC_PRESSURE = 4
LATEST_DATA_LONG_SOUND_NOISE = 5
LATEST_DATA_LONG_ETVOC = 6
LATEST_DATA_LONG_ECO2 = 7
LATEST_DATA_LONG_DISCOMFORT_INDEX = 8
LATEST_DATA_LONG_HEAT_STROKE = 9
LATEST_DATA_LONG_VIBRATION_INFORMATION = 10
LATEST_DATA_LONG_SI_VALUE = 11
LATEST_DATA_LONG_PGA = 12
LATEST_DATA_LONG_SEISMIC_INTENSITY = 13
LATEST_DATA_LONG_TEMPERATURE_FLAG = 14
LATEST_DATA_LONG_RELATIVE_HUMIDITY_FLAG = 15
LATEST_DATA_LONG_AMBIENT_LIGHT_FLAG = 16
LATEST_DATA_LONG_BAROMETRIC_PRESSURE_FLAG = 17
LATEST_DATA_LONG_SOUND_NOISE_FLAG = 18
LATEST_DATA_LONG_ETVOC_FLAG = 19
LATEST_DATA_LONG_ECO2_FLAG = 20
LATEST_DATA_LONG_DISCOMFORT_INDEX_FLAG = 21
LATEST_DATA_LONG_HEAT_STROKE_FLAG = 22
LATEST_DATA_LONG_SI_VALUE_FLAG = 23
LATEST_DATA_LONG_PGA_FLAG = 24
LATEST_DATA_LONG_SEISMIC_INTENSITY_FLAG = 25

def s16(value):
	return -(value & 0x8000) | (value & 0x7fff)

def calc_crc(buf, length):
	crc = 0xFFFF
	for i in range(length):
		crc = crc ^ buf[i]
		for i in range(8):
			carrayFlag = crc & 1
			crc = crc >> 1
			if (carrayFlag == 1):
				crc = crc ^ 0xA001
	crcH = crc >> 8
	crcL = crc & 0x00FF
	return (bytearray([crcL, crcH]))

def crc16(buf: bytes) -> bytearray:
	length = len(buf)
	crc = 0xFFFF
	for i in range(length):
		crc ^= buf[i]
		for i in range(8):
			carrayFlag = crc & 1
			crc >>= 1
			if (carrayFlag == 1):
				crc ^= 0xA001
	return bytearray([crc & 0x00FF, crc >> 8])

def serial_write(ser, payload):
	command = b'\x52\x42' + pack('

環境センサー

環境センサーの準備

OMRONモジュールで値を取得するために以下の環境センサーを使用します。

センサー値はPythonでは取得出来たがSENSOR関数 (スクリプト関数)では取得出来なかったので、UWSCからPythonを経由して値を取得します。

Pythonがインストールされていない場合は、Pythonのインストールを参考にインストールを済ましておいてください。

2JCIE-BU01で取得できる項目は以下のとおりです。

  1. 温度
  2. 湿度
  3. 照度
  4. 気圧
  5. 騒音
  6. 3軸加速度
  7. eTVOC
  8. 不快指数
  9. 熱中症警戒度
  10. 振動情報(地震回数、振動回数、SI値)

詳しい製品情報については2JCIE-BU 環境センサ(USB型)/形式/種類 | オムロン制御機器をご覧ください。

マニュアルは形2JCIE-BU01 環境センサ(USB型) ユーザーズマニュアルを参照。

USBドライバーのインストール

Windowsでこの環境センサーからのデータを取得するには、USBドライバーをインストールする必要があります。USBドライバーとは、USBデバイスをパソコンに接続して使用できるようにするソフトウェアのことです。

形2JCIEシリーズ ソフトウェアダウンロード使用許諾 | オムロン電子部品サイト - Japanを開き、ご承諾事項に同意しますにチェックを入れダウンロードページを開きます。

ダウンロードファイル環境センサ (USB型)USBドライバにある2jcie-bu01_usbdriver.zipをダウンロード、解凍します。

インストールの手順は環境センサ(USB 型)(2JCIE-BU01) ドライバーインストール説明書のPDFファイルをダウンロードし参考にしてください。

環境センサ(2JCIE-BU01)をパソコンのUSBポートに接続します。

スタートを右クリックしメニューからデバイス マネージャーを開きます。

Windows\ほかのデバイス\2JCIE-BU01を右クリックしドライバーの更新を選択します。

ドライバーの検索方法が表示されるので、コンピューターを参照してドライバーを検索を選択します。

参照で解凍した2jcie-bu01_usbdriverのフォルダを指定しを選択します。

ドライバーが正常に更新されましたと表示されるので、閉じるを選択します。

問題なく更新できていればWindows\ユニバーサル シリアル バス コントローラー\OMRON Environment Sensor 2JCIE-BU01が追加されます。

Windows\ほかのデバイス\USB Serial Portを選択し、ドライバーの更新を選択します。

ドライバーの検索方法が表示されるので、コンピューターを参照してドライバーを検索を選択します。

参照で解凍した2jcie-bu01_usbdriverのフォルダを指定しを選択します。

ドライバーが正常に更新されましたと表示されるので、閉じるを選択します。

Windows\ポート(COMとLPT)2JCIE-BU01 Serial Port (COM3)と表示されていればドライバーのインストールは完了です。

Pythonのインストール

まずDownload Python | Python.orgでPythonをダウンロードし、EXEファイルを実行します。

Use admin privileges when installing py.exeAdd python.exe to PATHにチェックを入れInstall Nowをクリックします。

それぞれ管理者権限でpy.exeをインストールpython.exeを環境変数に追加という意味です。

Install Nowのすぐ下に記載されてるC:\Users\akita\AppData\Local\Programs\Python\Python313にインストールされます。これはユーザー名やインストールするバージョンによって変わります。

すぐにインストール処理が開始されます。

ユーザー アカウント制御が表示されたらはいを選択し、デバイスに変更を加えることを許可します。

インストールが続行されるので、完了するまでしばらく(5分程度)待機します。

Setup was successfulと表示されたらインストールは完了です。Disable path length limitはクリックせずCloseを選択し終了します。

Pythonのパスを通す

インストール時にAdd python.exe to PATHを選択していれば以下の手順は不要です。

スタートを右クリック、システムシステムの詳細設定環境変数*** のユーザー環境変数の変数名Pathを選択して編集環境変数名の編集ウィンドウが開くので、新規で以下の2つを追加します。

usernameにはユーザー名、Python313はインストールするPythonのバージョンによって変わってくるので適宜修正してください。

プレーンテキスト
プレーンテキスト

パスが通っていればコマンドプロンプトでpythonと実行したときにバージョンが表示されます。

コマンドプロンプト
結果
プレーンテキスト

スクリプトの準備

Pythonのインストールが完了したら、実際にスクリプトを実行してみます。ここでは、オムロン公式がGitHubで公開しているPythonのスクリプトを実行してみます。GitHub - omron-devhub/2jciebu-usb-raspberrypi · GitHubを開きCodeDownload ZIPをクリックしZIPファイル(2jciebu-usb-raspberrypi-master.zip)をダウンロードします。

解凍したらsample_2jciebu.pyをメモ帳などのテキストファイルで開き、の103行目(serialモジュールSerial関数)を以下のように/dev/ttyUSB0からCOM3に書き換えます。Serial関数の第一引数は接続するシリアルポートを表していて、シリアルポートはUSBドライバーのインストールが完了したときにWindows\ポート(COM と LPT)2JCIE-BU01 Serial Port後ろの括弧内に記載されているので、COM3に修正して動作しない場合はこちらを確認してください。

Python

Python

またsample_2jciebu.pyを実行するにはpySerialというパッケージが必要で事前にインストールしておく必要があります。コマンドプロンプトもしくはPowerShellで以下のプログラムを実行することでインストールができます。

コマンドプロンプト

スクリプトの実行

コマンドプロンプトでpythonの後にスクリプトのパス名を指定することでスクリプトを実行することができます。解凍してシリアルポートを編集したsample_2jciebu.pyのパス名を指定してください。

python、半角スペースと入力したあとにファイルをドラッグアンドドロップで指定することできます。

コマンドプロンプト
結果
プレーンテキスト

以下のようなエラーが出る場合も、何度か試していると結果を取得できることもあります。

結果
プレーンテキスト

PySerialのインストール

モジュールが見つからずインポート出来なった場合は、以下のようなエラーが発生します。今回はModuleNotFoundError: No module named 'serial'というシリアル通信を行うためのライブラリが読み込めていません。

コマンドプロンプト
結果
プレーンテキスト

PySerialはPythonでシリアル通信を行うためのライブラリが必要で以下のプログラムをコマンドプロンプトまたはPowerShellで実行しインストールします。

コマンドプロンプト
結果
プレーンテキスト

アンインストールするにはコマンドプロンプトまたはPowerShellで以下のプログラムを実行します。

コマンドプロンプト
結果
プレーンテキスト

サンプルプログラムの実行

ユニバーサル シリアル バス コントローラーOMRON Environment Sensor 2JCIE-BU01も同様にドライバーを更新します。

ドライバーをインストールする前だとエラーが発生します。

コマンドプロンプト
結果
プレーンテキスト

コマンドプロンプトで以下を実行しエラーが発生する場合は、ドライバーのインストールができていません。

コマンドプロンプト
結果
プレーンテキスト
結果
プレーンテキスト

Stirling

Pythonではバイナリデータを扱うのでStirlingの詳細情報 : Vector ソフトを探す!をインストールしておくと良いです。

USB通信

通信仕様

USB通信の通信仕様は以下の表の通りです。形2JCIE-BU01 環境センサ(USB型) ユーザーズマニュアルP69を参照。

項目(Item)仕様(Spec)
通信速度(Baud rate)115200bps
データ長(Data size)8bit
ストップビット(Stop bit)1bit
パリティ(Parity)なし
フロー制御(Flow Control)なし

フレーム形式

共通フレーム形式

USB通信で送受信する際のフレーム形式を以下に示します。2バイト以上のデータはすべてリトルエンディアンで記述します。

HeaderLengthPayloadCRC-16
2 byte2 byteN byte2 byte
0x520x42L byteH byte-L byteH byte
Header
フレームの開始を表す。ASCIIコードのBR(0x4252)で固定
Length
PayloadからCRC(末尾)までのデータ長をリトルエンディアンで指定
Payload
コマンドに応じたフレームを設定。Payload部はUSB originalの要求内容、BLE commonの要求内容によって異なる。ペイロードフレーム形式を参照。
CRC-16
HeaderからPayload末尾までのCRC結果をリトルエンディアンで指定
巡回冗長検査(CRC-16)の計算

CRC-16CRC-16-IBMとも呼ばれる)は巡回冗長検査の一種で、データ通信でデータの誤りを検出するのに使用されます。

生成多項式
生成多項式とは、巡回冗長検査でデータの誤りを検知するための多項式です。送信データをこの多項式で割った結果を付与し、受信側でその値を再計算することでデータの誤りを検知することができます。CRC-16-IBMの生成多項式は以下に示すとおりです。 \[x^{16}+x^{15}+x^{2}+1\] 生成多項式そのままでは計算に使用できないので、生成多項式からバイナリ形式に変換した値を求めます。
まず生成多項式の係数が0になる項も省略せずに定数項まで書き出します。 \[1x^{16}+1x^{15}+0x^{14}+0x^{13}+0x^{12}+0x^{11}+0x^{10}+0x^{9}+0x^{8}+0x^{7}+0x^{6}+0x^{5}+0x^{4}+0x^{3}+1x^{2}+0x+1 \times 1\] この多項式の項と係数を分けると以下の表のようになります。CRC-16のとき\(x^{16}\)の係数は必ず1になるので省略。
係数1000000000000101
\(x^{15}\)\(x^{14}\)\(x^{13}\)\(x^{12}\)\(x^{11}\)\(x^{10}\)\(x^{9}\)\(x^{8}\)\(x^{7}\)\(x^{6}\)\(x^{5}\)\(x^{4}\)\(x^{3}\)\(x^{2}\)\(x\)1
この係数部分のみを抽出し次数の高い順に並べバイナリ形式(2進数)で表すと0b1000 0000 0000 0101で16進数では0x8005となります。
係数1010000000000001
1\(x\)\(x^{2}\)\(x^{3}\)\(x^{4}\)\(x^{5}\)\(x^{6}\)\(x^{7}\)\(x^{8}\)\(x^{9}\)\(x^{10}\)\(x^{11}\)\(x^{12}\)\(x^{13}\)\(x^{14}\)\(x^{15}\)
一方、次数の低い順に並べると0b1010 0000 0000 0001で16進数では0xA001となります。
初期値
0xFFFF

巡回冗長検査を求める手順を以下に示します。

  1. CRCレジスタの初期値を0xFFFFにする。
  2. CRCレジスタとメッセージの初めの8bitデータのXORを計算し、結果をCRCレジスタに戻す。
  3. MSBは0で埋めながら、CRCレジスタを1bit右シフトする。
  4. LSBからシフトされたbitが0ならば、3の手順を繰り返す。1ならばCRCレジスタと0xA001でXORを計算し、結果をCRCレジスタに戻す。
  5. 8bit分シフトするまで3、4の手順を繰り返す。
  6. パケットの最後まで処理していなければ、CRCレジスタとパケットの次の8bitのXORを計算してCRCレジスタに戻し、3の手順から繰り返す。

CRC-16を求めるPythonのブログラムです。

Python

ブラウザ上で値を確認したい場合は、以下のテキストボックスにデータを入れることでCRC-16を求めることができます。

サンプルとして表示している0x52 0x42 0x05 0x00 0x01 0x0A 0x184.5.25 Device information (Address: 0x180A)のデバイス情報を取得するときのコマンドで、HeaderLengthPayloadの項目を表しています。

コマンドとして送信するにはこれらの末尾にCRC-16を付与する必要があります。0x52 0x42 0x05 0x00 0x01 0x0A 0x18のCRC-16を求めると0x8DFCとなり、下位ビットから8bitずつ並べるとCRC-16を含めたコマンドは0x52 0x42 0x05 0x00 0x01 0x0A 0x18 0xFC 0x8Dとなります。

ペイロードフレーム形式

共通フレーム形式Payloadに以下のCommandAddressData(エラー発生時はCode)が入ります。

ホストコントローラ

コマンドを送信するときのペイロードフレーム形式です。

CommandAddressData
1 byte2 byteN byte
-L byteH byteL byteH byte
Command
ReadもしくはWriteを指定
CommandContents
0x01Read
0x02Write
Address
実行する内容に応じたAddressをリトルエンディアン形式で指定
Data
Addressに応じた内容をリトルエンディアン形式で指定
レスポンスのフォーマット

コマンドを送信して問題なくデータが取得できたときのペイロードフレーム形式です。

CommandAddressData
1 byte2 byteN byte
-L byteH byteL byteH byte
Command
ReadもしくはWriteを返します。
CommandContents
0x01Read
0x02Write
Address
ホストコントローラで指定されたAddressを返します。
Data
ホストコントローラで指定されたAddressDataによって取得された内容に対応したDataを返します。
エラー時のフォーマット

問題が発生しデータを正常に取得できなかったときのペイロードフレーム形式です。

CommandAddressCode
1 byte2 byte1 byte
-L byteH byte-
Command
ReadもしくはWriteを返します。エラー発生時はCommandMSB1とした値(0x80を加えた値)となります。ReadWrite以外のCommandを受信した場合はUnknown(0xFF)を返します。
CommandContents
0x81Read error
0x82Write error
0xFFUnknown
Address
ホストコントローラで指定されたAddressを返します。
Code
発生したエラー内容に対応したコードを返します。
CodeDescriptionContents
0x01CRC errorCRC-16計算が誤っている場合
0x02Command errorCommandにRead, Write以外が指定された場合
0x03Address errorAddress listに記載されていないAddressが指定された場合
0x04Length errorAddressで指定されているLengthが間違っている場合
0x05Data errorWriteの書き込み範囲外を指定された場合
0x06BusyFLASH Memoryアクセス中など内部処理している場合
コマンドの生成

共通フレーム形式ペイロードフレーム形式を組み合わせた全体のフレーム形式を以下にそれぞれ示します。

ホストコントローラもしくはレスポンス

共通フレーム形式とペイロードフレーム形式は以下のとおりです。

共通フレーム形式
HeaderLengthPayloadCRC-16
2 byte2 byteN byte2 byte
0x520x42L byteH byte-L byteH byte
ペイロードフレーム形式(ホストコントローラもしくはレスポンス)
CommandAddressData
1 byte2 byteN byte
-L byteH byteL byteH byte

共通フレーム形式のPayloadにペイロードフレーム形式(ホストコントローラ)を代入すると、コマンドを送受信するときのフレーム形式は以下のようになります。

HeaderLengthPayloadCRC-16
CommandAddressData
2 byte2 byte1 byte2 byteN byte2 byte
0x520x42L byteH byte-L byteH byteL byteH byteL byteH byte
エラー時

エラー時はレスポンスでDataを返していた部分が1 byteのCodeになります。

共通フレーム形式
HeaderLengthPayloadCRC-16
2 byte2 byteN byte2 byte
0x520x42L byteH byte-L byteH byte
ペイロードフレーム形式(エラー時)
CommandAddressCode
1 byte2 byte1 byte
-L byteH byte-

HeaderLengthPayloadCRC-16
CommandAddressCode
2 byte2 byte1 byte2 byte1 byte2 byte
0x520x42L byteH byte-L byteH byte-L byteH byte
取得例

デバイスの情報(形2JCIE-BU01 環境センサ(USB型) ユーザーズマニュアルのP101「4.5.25 Device information (Address: 0x180A)」を参照)を取得するときは以下のようになります。

HeaderLengthPayloadCRC-16
CommandAddressData
2 byte2 byte1 byte2 byte0 byte2 byte
0x520x420x050x000x010x0A0x180xFC0x8D
Header
0x5242で固定
Length
PayloadCRC-16のデータ長の合計である5 byte(=1+2+0+2)を指定。0x0005なので0x050x00とリトルエンディアン形式で指定
Command
取得するときは0x01(Read)を指定
Address
Device informationAddress0x180Aなので0x0A0x18とリトルエンディアン形式で指定
Data
Device informationで取得時Dataを指定する必要ないので空欄

使い方

UWSC
結果
プレーンテキスト

参考文献

  1. 環境センサをカスタマイズする | Pi Zero Easy IoT
  2. ESP32からBLEでオムロン環境センサ(2JCIE-BL)に繋ぐ #OMRON - Qiita
  3. BLE Scanner (Connect & Notify) - Google Play のアプリ
  4. https://ja.wikipedia.org/wiki/%E5%B7%A1%E5%9B%9E%E5%86%97%E9%95%B7%E6%A4%9C%E6%9F%BB
  5. 16 bit CRC Calculation || Error Detection|| Modbus RTU || Using Reflected (or Reverse) Polynomial - YouTube
  6. http://wwwa.pikara.ne.jp/polaris/CRC16-IBM.pdf