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('ltH', len(payload) + 2) + payload
	command = command + calc_crc(command, len(command))
	ser.write(command)
	ser.flush()
	return

args = sys.argv

# Serial
ser = serial.Serial("COM3", 115200, serial.EIGHTBITS, serial.PARITY_NONE)

match args[1]:
	case "180A":
		if os.path.exists("180A.bin") and bool(args[3]):
			with open("180A.bin", "rb") as f:
				data = f.read()
		else:
			payload = bytearray([
				0x01,				# Read
				0x0a, 0x18			# 180a
			])
			serial_write(ser, payload)
			_ser_len = ser.inWaiting()
			while _ser_len == 0:
				time.sleep(0.1)
				_ser_len = ser.inWaiting()
			data = ser.read(_ser_len)
		if args[2] == DEVICE_INFORMATION_MODEL_NUMBER:
			result = data[7:17].decode()
		elif args[2] == DEVICE_INFORMATION_SERIAL_NUMBER:
			result = data[17:27].decode()
		elif args[2] == DEVICE_INFORMATION_FIRMWARE_REVISION:
			result = data[27:32].decode()
		elif args[2] == DEVICE_INFORMATION_HARDWARE_REVISION:
			result = data[32:37].decode()
		elif args[2] == DEVICE_INFORMATION_MANUFACTURE_NAME:
			result = data[37:42].decode()
		elif int(args[2]) == OMRON_GET_DATA:
			# 空の辞書を作成
			dict = {}
			# 新しいキーと値を追加
			dt = datetime.datetime.now()
			dict['DateTime'] = dt.strftime('%Y/%m/%d %H:%M:%S')
			dict['ModelNumber'] = data[7:17].decode()
			dict["SerialNumber"] = data[17:27].decode()
			dict["FirmwareRevision"] = data[27:32].decode()
			dict["HardwareRevision"] = data[32:37].decode()
			dict["ManufacturerName"] = data[37:42].decode()
			# 辞書をJSON形式に変換
			json_data = json.dumps(dict, ensure_ascii=False, indent=4)
			json.dump(dict, open('180A.json', 'w'), ensure_ascii=False, indent=4, sort_keys=False)
			result = data
		print(result, end="")
	case "5012":
		if os.path.exists("5012.bin") and bool(args[3]):
			with open("5012.bin", "rb") as f:
				data = f.read()
		else:
			payload = bytearray([
				0x01,
				0x12, 0x50
			])
			serial_write(ser, payload)
			_ser_len = ser.inWaiting()
			while _ser_len == 0:
				time.sleep(0.1)
				_ser_len = ser.inWaiting()
			data = ser.read(_ser_len)
		if args[2] == LATEST_SENSING_DATA_SEQUENCE_NUMBER:
			result = data[7]
		elif args[2] == LATEST_SENSING_DATA_TEMPERATURE:
			result = str( s16(int(hex(data[9]) + '{:02x}'.format(data[8], 'x'), 16)) / 100)
		elif args[2] == LATEST_SENSING_DATA_RELATIVE_HUMIDITY:
			result = str(int(hex(data[11]) + '{:02x}'.format(data[10], 'x'), 16) / 100)
		elif args[2] == LATEST_SENSING_DATA_AMBIENT_LIGHT:
			result = str(int(hex(data[13]) + '{:02x}'.format(data[12], 'x'), 16))
		elif args[2] == LATEST_SENSING_DATA_BAROMETRIC_PRESSURE:
			result = str(int(hex(data[17]) + '{:02x}'.format(data[16], 'x') + '{:02x}'.format(data[15], 'x') + '{:02x}'.format(data[14], 'x'), 16) / 1000)
		elif args[2] == LATEST_SENSING_DATA_SOUND_NOISE:
			result = str(int(hex(data[19]) + '{:02x}'.format(data[18], 'x'), 16) / 100)
		elif args[2] == LATEST_SENSING_DATA_ETVOC:
			result = str(int(hex(data[21]) + '{:02x}'.format(data[20], 'x'), 16))
		elif args[2] == LATEST_SENSING_DATA_ECO2:
			result = str(int(hex(data[23]) + '{:02x}'.format(data[22], 'x'), 16))
		elif int(args[2]) == OMRON_GET_DATA:
			dict = {}
			dt = datetime.datetime.now()
			dict['DateTime'] = dt.strftime('%Y/%m/%d %H:%M:%S')
			dict['SequenceNumber'] = data[7]
			dict['Temperature'] = str( s16(int(hex(data[9]) + '{:02x}'.format(data[8], 'x'), 16)) / 100)
			dict['RelativeHumidity'] = str(int(hex(data[11]) + '{:02x}'.format(data[10], 'x'), 16) / 100)
			dict['AmbientLight'] = str(int(hex(data[13]) + '{:02x}'.format(data[12], 'x'), 16))
			dict['BarometricPressure'] = str(int(hex(data[17]) + '{:02x}'.format(data[16], 'x') + '{:02x}'.format(data[15], 'x') + '{:02x}'.format(data[14], 'x'), 16) / 1000)
			dict['SoundNoise'] = str(int(hex(data[19]) + '{:02x}'.format(data[18], 'x'), 16) / 100)
			dict['eTVOC'] = str(int(hex(data[21]) + '{:02x}'.format(data[20], 'x'), 16))
			dict['eCO2'] = str(int(hex(data[23]) + '{:02x}'.format(data[22], 'x'), 16))
			json_data = json.dumps(dict, ensure_ascii=False, indent=4)
			json.dump(dict, open('5012.json', 'w'), ensure_ascii=False, indent=4, sort_keys=False)
			result = data
		print(result, end="")
	case "5013":
		if os.path.exists("5013.bin") and bool(args[3]):
			with open("5013.bin", "rb") as f:
				data = f.read()
		else:
			payload = bytearray([
				0x01,
				0x13, 0x50
			])
			serial_write(ser, payload)
			_ser_len = ser.inWaiting()
			while _ser_len == 0:
				time.sleep(0.1)
				_ser_len = ser.inWaiting()
			data = ser.read(_ser_len)
		if args[2] == LATEST_CALCULATION_DATA_SEQUENCE_NUMBER:
			result = data[7]
		elif args[2] == LATEST_CALCULATION_DATA_DISCOMFORT_INDEX:
			result = str( s16(int(hex(data[9]) + '{:02x}'.format(data[8], 'x'), 16)) / 100)
		elif args[2] == LATEST_CALCULATION_DATA_HEAT_STROKE:
			result = str( s16(int(hex(data[11]) + '{:02x}'.format(data[10], 'x'), 16)) / 100)
		elif args[2] == LATEST_CALCULATION_DATA_VIBRATION_INFORMATION:
			result = data[12]
		elif args[2] == LATEST_CALCULATION_DATA_SI_VALUE:
			result = str( s16(int(hex(data[14]) + '{:02x}'.format(data[13], 'x'), 16)) / 10)
		elif args[2] == LATEST_CALCULATION_DATA_PGA:
			result = str( s16(int(hex(data[16]) + '{:02x}'.format(data[15], 'x'), 16)) / 10)
		elif args[2] == LATEST_CALCULATION_DATA_SEISMIC_INTENSITY:
			result = str( s16(int(hex(data[18]) + '{:02x}'.format(data[17], 'x'), 16)) / 1000)
		elif args[2] == LATEST_CALCULATION_DATA_ACCELERATION_X_AXIS:
			result = str( s16(int(hex(data[20]) + '{:02x}'.format(data[19], 'x'), 16)))
		elif args[2] == LATEST_CALCULATION_DATA_ACCELERATION_X_AXIS:
			result = str( s16(int(hex(data[22]) + '{:02x}'.format(data[21], 'x'), 16)))
		elif args[2] == LATEST_CALCULATION_DATA_ACCELERATION_X_AXIS:
			result = str( s16(int(hex(data[24]) + '{:02x}'.format(data[23], 'x'), 16)))
		elif int(args[2]) == OMRON_GET_DATA:
			dict = {}
			dt = datetime.datetime.now()
			dict['DateTime'] = dt.strftime('%Y/%m/%d %H:%M:%S')
			dict['SequenceNumber'] = data[7]
			dict['DiscomfortIndex'] = str( s16(int(hex(data[9]) + '{:02x}'.format(data[8], 'x'), 16)) / 100)
			dict['HeatStroke'] = str( s16(int(hex(data[11]) + '{:02x}'.format(data[10], 'x'), 16)) / 100)
			dict['VibrationInformation'] = data[12]
			dict['SIValue'] = str( s16(int(hex(data[14]) + '{:02x}'.format(data[13], 'x'), 16)) / 10)
			dict['PGA'] = str( s16(int(hex(data[16]) + '{:02x}'.format(data[15], 'x'), 16)) / 10)
			dict['SeismicIntensity'] = str( s16(int(hex(data[18]) + '{:02x}'.format(data[17], 'x'), 16)) / 1000)
			dict['AccelerationXAxis'] = str( s16(int(hex(data[20]) + '{:02x}'.format(data[19], 'x'), 16)))
			dict['AccelerationYAxis'] = str( s16(int(hex(data[22]) + '{:02x}'.format(data[21], 'x'), 16)))
			dict['AccelerationZAxis'] = str( s16(int(hex(data[24]) + '{:02x}'.format(data[23], 'x'), 16)))
			json_data = json.dumps(dict, ensure_ascii=False, indent=4)
			json.dump(dict, open('5013.json', 'w'), ensure_ascii=False, indent=4, sort_keys=False)
			result = data
		print(result, end="")
	case "5021":
		payload = bytearray([
			0x01, 0x21, 0x50
		])
		serial_write(ser, payload)
		time.sleep(0.1)
		data = ser.read(ser.inWaiting())
		if args[2] == LATEST_DATA_LONG_TEMPERATURE:
			result = str(s16(int(hex(data[9]) + '{:02x}'.format(data[8], 'x'), 16)) / 100)
		elif args[2] == LATEST_DATA_LONG_RELATIVE_HUMIDITY:
			result = str(int(hex(data[11]) + '{:02x}'.format(data[10], 'x'), 16) / 100)
		elif args[2] == LATEST_DATA_LONG_AMBIENT_LIGHT:
			result = str(int(hex(data[13]) + '{:02x}'.format(data[12], 'x'), 16))
		elif args[2] == LATEST_DATA_LONG_BAROMETRIC_PRESSURE:
			result = str(int(hex(data[17]) + '{:02x}'.format(data[16], 'x') + '{:02x}'.format(data[15], 'x') + '{:02x}'.format(data[14], 'x'), 16) / 1000)
		elif args[2] == LATEST_DATA_LONG_SOUND_NOISE:
			result = str(int(hex(data[19]) + '{:02x}'.format(data[18], 'x'), 16) / 100)
		elif args[2] == LATEST_DATA_LONG_ETVOC:
			result = str(int(hex(data[21]) + '{:02x}'.format(data[20], 'x'), 16))
		elif args[2] == LATEST_DATA_LONG_ECO2:
			result = str(int(hex(data[23]) + '{:02x}'.format(data[22], 'x'), 16))
		elif args[2] == LATEST_DATA_LONG_DISCOMFORT_INDEX:
			result = str(int(hex(data[25]) + '{:02x}'.format(data[24], 'x'), 16) / 100)
		elif args[2] == LATEST_DATA_LONG_HEAT_STROKE:
			result = str(s16(int(hex(data[27]) + '{:02x}'.format(data[26], 'x'), 16)) / 100)
		elif args[2] == LATEST_DATA_LONG_VIBRATION_INFORMATION:
			result = str(int(hex(data[28]), 16))
		elif args[2] == LATEST_DATA_LONG_SI_VALUE:
			result = str(int(hex(data[30]) + '{:02x}'.format(data[29], 'x'), 16) / 10)
		elif args[2] == LATEST_DATA_LONG_PGA:
			result = str(int(hex(data[32]) + '{:02x}'.format(data[31], 'x'), 16) / 10)
		elif args[2] == LATEST_DATA_LONG_SEISMIC_INTENSITY:
			result = str(int(hex(data[34]) + '{:02x}'.format(data[33], 'x'), 16) / 1000)
		elif args[2] == LATEST_DATA_LONG_TEMPERATURE_FLAG:
			result = str(int(hex(data[36]) + '{:02x}'.format(data[35], 'x'), 16))
		elif args[2] == LATEST_DATA_LONG_RELATIVE_HUMIDITY_FLAG:
			result = str(int(hex(data[38]) + '{:02x}'.format(data[37], 'x'), 16))
		elif args[2] == LATEST_DATA_LONG_AMBIENT_LIGHT_FLAG:
			result = str(int(hex(data[40]) + '{:02x}'.format(data[39], 'x'), 16))
		elif args[2] == LATEST_DATA_LONG_BAROMETRIC_PRESSURE_FLAG:
			result = str(int(hex(data[42]) + '{:02x}'.format(data[41], 'x'), 16))
		elif args[2] == LATEST_DATA_LONG_SOUND_NOISE_FLAG:
			result = str(int(hex(data[44]) + '{:02x}'.format(data[43], 'x'), 16))
		elif args[2] == LATEST_DATA_LONG_ETVOC_FLAG:
			result = str(int(hex(data[46]) + '{:02x}'.format(data[45], 'x'), 16))
		elif args[2] == LATEST_DATA_LONG_ECO2_FLAG:
			result = str(int(hex(data[48]) + '{:02x}'.format(data[47], 'x'), 16))
		elif args[2] == LATEST_DATA_LONG_DISCOMFORT_INDEX_FLAG:
			result = str(int(hex(data[50]) + '{:02x}'.format(data[49], 'x'), 16))
		elif args[2] == LATEST_DATA_LONG_HEAT_STROKE_FLAG:
			result = str(int(hex(data[52]) + '{:02x}'.format(data[51], 'x'), 16))
		elif args[2] == LATEST_DATA_LONG_SI_VALUE_FLAG:
			result = str(int(hex(data[53]), 16))
		elif args[2] == LATEST_DATA_LONG_PGA_FLAG:
			result = str(int(hex(data[54]), 16))
		elif args[2] == LATEST_DATA_LONG_SEISMIC_INTENSITY_FLAG:
			result = str(int(hex(data[55]), 16))
		elif int(args[2]) == OMRON_GET_DATA:
				dict = {}
				dt = datetime.datetime.now()
				dict['DateTime'] = dt.strftime('%Y/%m/%d %H:%M:%S')
				dict["SequenceNumber"] = data[7]
				dict["Temperature"] = str( s16(int(hex(data[9]) + '{:02x}'.format(data[8], 'x'), 16)) / 100)
				dict["RelativeHumidity"] = str(int(hex(data[11]) + '{:02x}'.format(data[10], 'x'), 16) / 100)
				dict["AmbientLight"] = str(int(hex(data[13]) + '{:02x}'.format(data[12], 'x'), 16))
				dict["BarometricPressure"] = str(int(hex(data[17]) + '{:02x}'.format(data[16], 'x') + '{:02x}'.format(data[15], 'x') + '{:02x}'.format(data[14], 'x'), 16) / 1000)
				dict["SoundNoise"] = str(int(hex(data[19]) + '{:02x}'.format(data[18], 'x'), 16) / 100)
				dict["eTVOC"] = str(int(hex(data[21]) + '{:02x}'.format(data[20], 'x'), 16))
				dict["eCO2"] = str(int(hex(data[23]) + '{:02x}'.format(data[22], 'x'), 16))
				dict["DiscomfortIndex"] = str(int(hex(data[25]) + '{:02x}'.format(data[24], 'x'), 16) / 100)
				dict["HeatStroke"] = str(s16(int(hex(data[27]) + '{:02x}'.format(data[26], 'x'), 16)) / 100)
				dict["VibrationInformation"] = str(int(hex(data[28]), 16))
				dict["SIValue"] = str(int(hex(data[30]) + '{:02x}'.format(data[29], 'x'), 16) / 10)
				dict["PGA"] = str(int(hex(data[32]) + '{:02x}'.format(data[31], 'x'), 16) / 10)
				dict["SeismicIntensity"] = str(int(hex(data[34]) + '{:02x}'.format(data[33], 'x'), 16) / 1000)
				dict["TemperatureFlag"] = str(int(hex(data[36]) + '{:02x}'.format(data[35], 'x'), 16))
				dict["RelativeHumidityFlag"] = str(int(hex(data[38]) + '{:02x}'.format(data[37], 'x'), 16))
				dict["AmbientLightFlag"] = str(int(hex(data[40]) + '{:02x}'.format(data[39], 'x'), 16))
				dict["BarometricPressureFlag"] = str(int(hex(data[42]) + '{:02x}'.format(data[41], 'x'), 16))
				dict["SoundNoiseFlag"] = str(int(hex(data[44]) + '{:02x}'.format(data[43], 'x'), 16))
				dict["eTVOCFlag"] = str(int(hex(data[46]) + '{:02x}'.format(data[45], 'x'), 16))
				dict["eCO2Flag"] = str(int(hex(data[48]) + '{:02x}'.format(data[47], 'x'), 16))
				dict["DiscomfortIndexFlag"] = str(int(hex(data[50]) + '{:02x}'.format(data[49], 'x'), 16))
				dict["HeatStrokeFlag"] = str(int(hex(data[52]) + '{:02x}'.format(data[51], 'x'), 16))
				dict["SIValueFlag"] = str(int(hex(data[53]), 16))
				dict["PGAFlag"] = str(int(hex(data[54]), 16))
				dict["SeismicIntensityFlag"] = str(int(hex(data[55]), 16))
				json_data = json.dumps(dict, ensure_ascii=False, indent=4)
				json.dump(dict, open('5021.json', 'w'), ensure_ascii=False, indent=4, sort_keys=False)
				result = data
		print(result, end="")

環境センサー

環境センサーの準備

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ポートに接続します。

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

1 デバイスマネージャー.png

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

2 デバイスマネージャー ほかのデバイス 2JCIE-BU01 右クリック.png

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

3 デバイスマネージャー ドライバーの更新 - 2JCIE-BU01 ドライバーの検索方法.png

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

4 デバイスマネージャー ドライバーの更新 - 2JCIE-BU01 コンピューター上のドライバーを参照します。.png

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

5 デバイスマネージャー ドライバーの更新 - OMRON Environment Sensor 2JCIE-BU01 ドライバーが正常に更新されました.png

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

6 デバイスマネージャー ユニバーサル シリアル バス コントローラー OMRON Environment Sensor 2JCIE-BU01.png

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

7 デバイスマネージャー ほかのデバイス USB Serial Port 右クリック.png

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

8 デバイスマネージャー ドライバーの更新 - USB Serial Port ドライバーの検索方法.png

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

9 デバイスマネージャー ドライバーの更新 - USB Serial Port コンピューター上のドライバーを参照します。.png

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

10 デバイスマネージャー ドライバーの更新 - 2JCIE-BU01 Serial Port (COM3) ドライバーが正常に更新されました.png

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

11 デバイスマネージャー ポート (COM と LPT)2JCIE-BU01 Serial Port (COM3).png

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にインストールされます。これはユーザー名やインストールするバージョンによって変わります。

1 Python 3.13.2 (64-bit) Setup - Install Python 3.13.2 (64-bit).png

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

2 Python 3.13.2 (64-bit) Setup - Setup Progress.png

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

3 ユーザー アカウント制御.png

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

4 Python 3.13.2 (64-bit) Setup - Setup Progress.png

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

4 Python 3.13.2 (64-bit) Setup - Setup was successful.png
Pythonのパスを通す

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

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

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

プレーンテキスト
C:\Users\username\AppData\Local\Programs\Python\Python313\Scripts\
プレーンテキスト
C:\Users\username\AppData\Local\Programs\Python\Python313\

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

コマンドプロンプト
python
結果
プレーンテキスト
Python 3.13.2 (tags/v3.13.2:4f8bb39, Feb  4 2025, 15:23:48) [MSC v.1942 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>>

スクリプトの準備

Pythonのインストールが完了したら、実際にスクリプトを実行してみます。ここでは、オムロン公式がGitHubで公開しているPythonのスクリプトを実行してみます。GitHub - omron-devhub/2jciebu-usb-raspberrypiを開き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
    ser = serial.Serial("/dev/ttyUSB0", 115200, serial.EIGHTBITS, serial.PARITY_NONE)

Python
    ser = serial.Serial("COM3", 115200, serial.EIGHTBITS, serial.PARITY_NONE)

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

コマンドプロンプト
pip install pySerial

スクリプトの実行

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

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

コマンドプロンプト
python "D:\Downloads\2jciebu-usb-raspberrypi-master\sample_2jciebu.py"
結果
プレーンテキスト
Time measured:2025/02/10 06:57:58
Temperature:14.92
Relative humidity:51.83
Ambient light:216
Barometric pressure:1010.248
Sound noise:72.16
eTVOC:15
eCO2:501
Discomfort index:58.62
Heat stroke:13.53
Vibration information:0
SI value:0.0
PGA:0.0
Seismic intensity:0.0
Temperature flag:0
Relative humidity flag:0
Ambient light flag:0
Barometric pressure flag:0
Sound noise flag:0
eTVOC flag:0
eCO2 flag:0
Discomfort index flag:0
Heat stroke flag:0
SI value flag:0
PGA flag:0
Seismic intensity flag:0

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

結果
プレーンテキスト
Traceback (most recent call last):
  File "D:\Downloads\2jciebu-usb-raspberrypi-master\sample_2jciebu.py", line 120, in <module>
    print_latest_data(data)
    ~~~~~~~~~~~~~~~~~^^^^^^
  File "D:\Downloads\2jciebu-usb-raspberrypi-master\sample_2jciebu.py", line 38, in print_latest_data
    temperature = str( s16(int(hex(data[9]) + '{:02x}'.format(data[8], 'x'), 16)) / 100)
                                   ~~~~^^^
IndexError: index out of range

Temperature温度
Relative humidity湿度
Ambient light照度
Barometric pressure気圧
Sound noise騒音
eTVOCeTVOC(総揮発性有機化合物濃度)
eCO2
Discomfort index不快指数
Heat stroke熱中症警戒度
Vibration information
SI valueSI値
PGA
Seismic intensity震度
Temperature flag
Relative humidity flag
Ambient light flag
Barometric pressure flag
Sound noise flag
eTVOC flag
eCO2 flag
Discomfort index flag
Heat stroke flag
SI value flag
PGA flag
Seismic intensity flag

PySerialのインストール

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

コマンドプロンプト
python D:\Downloads\2jciebu-usb-raspberrypi-master\2jciebu-usb-raspberrypi-master\sample_2jciebu.py
結果
プレーンテキスト
Traceback (most recent call last):
  File "D:\Downloads\2jciebu-usb-raspberrypi-master\2jciebu-usb-raspberrypi-master\sample_2jciebu.py", line 1, in <module>
    import serial
ModuleNotFoundError: No module named 'serial'

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

コマンドプロンプト
pip install pyserial
結果
プレーンテキスト
Defaulting to user installation because normal site-packages is not writeable
Collecting pyserial
  Downloading pyserial-3.5-py2.py3-none-any.whl.metadata (1.6 kB)
Downloading pyserial-3.5-py2.py3-none-any.whl (90 kB)
   ---------------------------------------- 90.6/90.6 kB 1.0 MB/s eta 0:00:00
Installing collected packages: pyserial
Successfully installed pyserial-3.5

[notice] A new release of pip is available: 24.1.2 -> 25.0
[notice] To update, run: C:\Users\akita\AppData\Local\Microsoft\WindowsApps\PythonSoftwareFoundation.Python.3.12_qbz5n2kfra8p0\python.exe -m pip install --upgrade pip

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

コマンドプロンプト
pip uninstall pyserial
結果
プレーンテキスト
Found existing installation: pyserial 3.5
Uninstalling pyserial-3.5:
  Would remove:
    c:\users\akita\appdata\local\packages\pythonsoftwarefoundation.python.3.12_qbz5n2kfra8p0\localcache\local-packages\python312\scripts\pyserial-miniterm.exe
    c:\users\akita\appdata\local\packages\pythonsoftwarefoundation.python.3.12_qbz5n2kfra8p0\localcache\local-packages\python312\scripts\pyserial-ports.exe
    c:\users\akita\appdata\local\packages\pythonsoftwarefoundation.python.3.12_qbz5n2kfra8p0\localcache\local-packages\python312\site-packages\pyserial-3.5.dist-info\*
    c:\users\akita\appdata\local\packages\pythonsoftwarefoundation.python.3.12_qbz5n2kfra8p0\localcache\local-packages\python312\site-packages\serial\*
Proceed (Y/n)? Y
  Successfully uninstalled pyserial-3.5

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

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

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

コマンドプロンプト
python D:\Downloads\2jciebu-usb-raspberrypi-master\2jciebu-usb-raspberrypi-master\sample_2jciebu.py
結果
プレーンテキスト
Traceback (most recent call last):
  File "D:\Downloads\2jciebu-usb-raspberrypi-master\2jciebu-usb-raspberrypi-master\sample_2jciebu.py", line 103, in <module>
    ser = serial.Serial("COM3", 115200, serial.EIGHTBITS, serial.PARITY_NONE)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\akita\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.12_qbz5n2kfra8p0\LocalCache\local-packages\Python312\site-packages\serial\serialwin32.py", line 33, in __init__
    super(Serial, self).__init__(*args, **kwargs)
  File "C:\Users\akita\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.12_qbz5n2kfra8p0\LocalCache\local-packages\Python312\site-packages\serial\serialutil.py", line 244, in __init__
    self.open()
  File "C:\Users\akita\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.12_qbz5n2kfra8p0\LocalCache\local-packages\Python312\site-packages\serial\serialwin32.py", line 64, in open
    raise SerialException("could not open port {!r}: {!r}".format(self.portstr, ctypes.WinError()))
serial.serialutil.SerialException: could not open port 'COM3': FileNotFoundError(2, '指定されたファイルが見つかりません 。', None, 2)

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

コマンドプロンプト
python D:\Downloads\2jciebu-usb-raspberrypi-master\2jciebu-usb-raspberrypi-master\sample_2jciebu.py
結果
プレーンテキスト
Traceback (most recent call last):
  File "D:\Downloads\2jciebu-usb-raspberrypi-master\2jciebu-usb-raspberrypi-master\sample_2jciebu.py", line 103, in <module>
    ser = serial.Serial("/dev/ttyUSB0", 115200, serial.EIGHTBITS, serial.PARITY_NONE)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\akita\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.12_qbz5n2kfra8p0\LocalCache\local-packages\Python312\site-packages\serial\serialwin32.py", line 33, in __init__
    super(Serial, self).__init__(*args, **kwargs)
  File "C:\Users\akita\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.12_qbz5n2kfra8p0\LocalCache\local-packages\Python312\site-packages\serial\serialutil.py", line 244, in __init__
    self.open()
  File "C:\Users\akita\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.12_qbz5n2kfra8p0\LocalCache\local-packages\Python312\site-packages\serial\serialwin32.py", line 64, in open
    raise SerialException("could not open port {!r}: {!r}".format(self.portstr, ctypes.WinError()))
serial.serialutil.SerialException: could not open port '/dev/ttyUSB0': FileNotFoundError(2, '指定されたパスが見つかりま せん。', None, 3)
結果
プレーンテキスト
Traceback (most recent call last):
  File "D:\Downloads\2jciebu-usb-raspberrypi-master\2jciebu-usb-raspberrypi-master\sample_2jciebu.py", line 103, in <module>
    ser = serial.Serial("COM3", 115200, serial.EIGHTBITS, serial.PARITY_NONE)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\akita\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.12_qbz5n2kfra8p0\LocalCache\local-packages\Python312\site-packages\serial\serialwin32.py", line 33, in __init__
    super(Serial, self).__init__(*args, **kwargs)
  File "C:\Users\akita\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.12_qbz5n2kfra8p0\LocalCache\local-packages\Python312\site-packages\serial\serialutil.py", line 244, in __init__
    self.open()
  File "C:\Users\akita\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.12_qbz5n2kfra8p0\LocalCache\local-packages\Python312\site-packages\serial\serialwin32.py", line 64, in open
    raise SerialException("could not open port {!r}: {!r}".format(self.portstr, ctypes.WinError()))
serial.serialutil.SerialException: could not open port 'COM3': FileNotFoundError(2, '指定されたファイルが見つかりません 。', None, 2)

Stirling

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

USB通信

通信仕様

形2JCIE-BU01 環境センサ(USB型) ユーザーズマニュアルP69)

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

フレーム形式

共通フレーム形式
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)の計算
  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の手順から繰り返す。
Online CRC-8 CRC-16 CRC-32 Calculator
Python
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]))
ペイロードフレーム形式

共通フレーム形式のPayload部に以下のCommandAddressDataが入ります。

ホストコントローラ

コマンドを送信するときの形式です。

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
Commandで指定されたAddressを指定
Data
Addressに応じた内容
エラー時のフォーマット

問題が発生しデータを正常に取得できなかったときの形式です。

CommandAddressCode
1 byte2 byte1 byte
-L byteH byte-
Command
Read, Writeの結果を返答。Error返答時はCommandのMSBを1とした値(0x80を加えた値)となる。Read, Write以外のCommandを受信した場合はUnknown(0xFF)を返答。
CommandContents
0x81Read error
0x82Write error
0xFFUnknown
Address
実行する内容に応じてAddressを指定
Code
Error内容を返答
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

この2つを組み合わせると、コマンドを送信するときのフレーム形式は以下のようになります。

HeaderLengthPayloadCRC-16
CommandAddressData
2 byte2 byte1 byte2 byteN byte2 byte
0x520x42L byteH byte-L byteH byteL byteH byteL byteH byte

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

HeaderLengthPayloadCRC-16
CommandAddressData
2 byte2 byte1 byte2 byte0 byte2 byte
0x520x420x050x000x010x0A0x180xFC0x8D

Header0x5242で固定、LengthCommandAddressDataCRC-16のデータ長の合計なので5 byte(=1+2+0+2)となります。Address0x180Aなので0x0A0x18Dataは取得するときは指定しません。

使い方

UWSC
PRINT "■Device Information(0x180A)"
OMRON.DeviceInformation(OMRON_GET_DATA)
PRINT "日時," + OMRON.DeviceInformation(OMRON_CREATION_TIME)
PRINT "モデル番号," + OMRON.DeviceInformation(DEVICE_INFORMATION_MODEL_NUMBER, bool)
PRINT "シリアル番号," + OMRON.DeviceInformation(DEVICE_INFORMATION_SERIAL_NUMBER, bool)
PRINT "ファームウェアバージョン," + OMRON.DeviceInformation(DEVICE_INFORMATION_FIRMWARE_REVISION, bool)
PRINT "ハードウェアバージョン," + OMRON.DeviceInformation(DEVICE_INFORMATION_HARDWARE_REVISION, bool)
PRINT "製造メーカー," + OMRON.DeviceInformation(DEVICE_INFORMATION_MANUFACTURE_NAME, bool)
OMRON.DeviceInformation(OMRON_CACHE_CLEAR)
PRINT "----------"

PRINT "■Latest Sensing Data(0x5012)"
OMRON.LatestSensingData(OMRON_GET_DATA)
PRINT "日時," + OMRON.LatestSensingData(OMRON_CREATION_TIME)
PRINT "シーケンス番号," + OMRON.LatestSensingData(LATEST_SENSING_DATA_SEQUENCE_NUMBER, bool)
PRINT "温度," + OMRON.LatestSensingData(LATEST_SENSING_DATA_TEMPERATURE, bool)
PRINT "相対湿度," + OMRON.LatestSensingData(LATEST_SENSING_DATA_RELATIVE_HUMIDITY, bool)
PRINT "照度," + OMRON.LatestSensingData(LATEST_SENSING_DATA_AMBIENT_LIGHT, bool)
PRINT "気圧," + OMRON.LatestSensingData(LATEST_SENSING_DATA_BAROMETRIC_PRESSURE, bool)
PRINT "騒音," + OMRON.LatestSensingData(LATEST_SENSING_DATA_SOUND_NOISE, bool)
PRINT "eTVOC," + OMRON.LatestSensingData(LATEST_SENSING_DATA_ETVOC, bool)
PRINT "eCO2," + OMRON.LatestSensingData(LATEST_SENSING_DATA_ECO2, bool)
OMRON.LatestSensingData(OMRON_CACHE_CLEAR)
PRINT "----------"

PRINT "■Latest Calculation Data(0x5013)"
OMRON.LatestCalculationData(OMRON_GET_DATA)
PRINT "日時," +  OMRON.LatestCalculationData(OMRON_CREATION_TIME)
PRINT "シーケンス番号," + OMRON.LatestCalculationData(LATEST_CALCULATION_DATA_SEQUENCE_NUMBER, bool)
PRINT "不快指数," + OMRON.LatestCalculationData(LATEST_CALCULATION_DATA_DISCOMFORT_INDEX, bool)
PRINT "熱中症," + OMRON.LatestCalculationData(LATEST_CALCULATION_DATA_HEAT_STROKE, bool)
PRINT "振動情報," + OMRON.LatestCalculationData(LATEST_CALCULATION_DATA_VIBRATION_INFORMATION, bool)
PRINT "SI値," + OMRON.LatestCalculationData(LATEST_CALCULATION_DATA_SI_VALUE, bool)
PRINT "PGA," + OMRON.LatestCalculationData(LATEST_CALCULATION_DATA_PGA, bool)
PRINT "震度," + OMRON.LatestCalculationData(LATEST_CALCULATION_DATA_SEISMIC_INTENSITY, bool)
PRINT "加速度(X軸)," + OMRON.LatestCalculationData(LATEST_CALCULATION_DATA_ACCELERATION_X_AXIS, bool)
PRINT "加速度(Y軸)," + OMRON.LatestCalculationData(LATEST_CALCULATION_DATA_ACCELERATION_Y_AXIS, bool)
PRINT "加速度(Z軸)," + OMRON.LatestCalculationData(LATEST_CALCULATION_DATA_ACCELERATION_Z_AXIS, bool)
OMRON.LatestCalculationData(OMRON_CACHE_CLEAR)
PRINT "----------"

PRINT "■Latest Data Long(0x5021)"
OMRON.LatestDataLong(OMRON_GET_DATA)
PRINT "シーケンス番号," + OMRON.LatestDataLong(LATEST_DATA_LONG_SEQUENCE_NUMBER)
PRINT "温度," + OMRON.LatestDataLong(LATEST_DATA_LONG_TEMPERATURE)
PRINT "相対湿度," + OMRON.LatestDataLong(LATEST_DATA_LONG_RELATIVE_HUMIDITY)
PRINT "照度," + OMRON.LatestDataLong(LATEST_DATA_LONG_AMBIENT_LIGHT)
PRINT "気圧," + OMRON.LatestDataLong(LATEST_DATA_LONG_BAROMETRIC_PRESSURE)
PRINT "騒音," + OMRON.LatestDataLong(LATEST_DATA_LONG_SOUND_NOISE)
PRINT "eTVOC," + OMRON.LatestDataLong(LATEST_DATA_LONG_ETVOC)
PRINT "eCO2," + OMRON.LatestDataLong(LATEST_DATA_LONG_ECO2)
PRINT "不快指数," + OMRON.LatestDataLong(LATEST_DATA_LONG_DISCOMFORT_INDEX)
PRINT "熱中症危険度," + OMRON.LatestDataLong(LATEST_DATA_LONG_HEAT_STROKE)
PRINT "振動情報," + OMRON.LatestDataLong(LATEST_DATA_LONG_VIBRATION_INFORMATION)
PRINT "SI値," + OMRON.LatestDataLong(LATEST_DATA_LONG_SI_VALUE)
PRINT "PGA," + OMRON.LatestDataLong(LATEST_DATA_LONG_PGA)
PRINT "計測震度相当値," + OMRON.LatestDataLong(LATEST_DATA_LONG_SEISMIC_INTENSITY)
PRINT "温度フラグ," + OMRON.LatestDataLong(LATEST_DATA_LONG_TEMPERATURE_FLAG)
PRINT "相対湿度フラグ," + OMRON.LatestDataLong(LATEST_DATA_LONG_RELATIVE_HUMIDITY_FLAG)
PRINT "照度フラグ," + OMRON.LatestDataLong(LATEST_DATA_LONG_AMBIENT_LIGHT_FLAG)
PRINT "気圧フラグ," + OMRON.LatestDataLong(LATEST_DATA_LONG_BAROMETRIC_PRESSURE_FLAG)
PRINT "騒音フラグ," + OMRON.LatestDataLong(LATEST_DATA_LONG_SOUND_NOISE_FLAG)
PRINT "eTVOCフラグ," + OMRON.LatestDataLong(LATEST_DATA_LONG_ETVOC_FLAG)
PRINT "eCO2フラグ," + OMRON.LatestDataLong(LATEST_DATA_LONG_ECO2_FLAG)
PRINT "不快指数フラグ," + OMRON.LatestDataLong(LATEST_DATA_LONG_DISCOMFORT_INDEX_FLAG)
PRINT "熱中症危険度フラグ," + OMRON.LatestDataLong(LATEST_DATA_LONG_HEAT_STROKE_FLAG)
PRINT "SI値フラグ," + OMRON.LatestDataLong(LATEST_DATA_LONG_SI_VALUE_FLAG)
PRINT "PGAフラグ," + OMRON.LatestDataLong(LATEST_DATA_LONG_PGA_FLAG)
PRINT "計測震度相当値フラグ," + OMRON.LatestDataLong(LATEST_DATA_LONG_SEISMIC_INTENSITY_FLAG)
結果
プレーンテキスト
■Device Information(0x180A)
日時,2025/03/05 03:24:03
モデル番号,2JCIE-BU01
シリアル番号,05X8MY0037
ファームウェアバージョン,00.69
ハードウェアバージョン,01.00
製造メーカー,OMRON
----------
■Latest Sensing Data(0x5012)
日時,2025/03/05 03:24:11
シーケンス番号,43
温度,14.27
相対湿度,55.21
照度,0
気圧,1029.57
騒音,72.61
eTVOC,78
eCO2,913
----------
■Latest Calculation Data(0x5013)
日時,2025/03/05 03:24:15
シーケンス番号,47
不快指数,57.83
熱中症,13.53
振動情報,0
SI値,0.0
PGA,0.0
震度,0.0
加速度(X軸),663
加速度(Y軸),9760
加速度(Z軸),-1203
----------
■Latest Data Long(0x5021)
シーケンス番号,51
温度,14.28
相対湿度,55.21
照度,0
気圧,1029.575
騒音,72.92
eTVOC,78
eCO2,913
不快指数,57.77
熱中症危険度,13.54
振動情報,0
SI値,0.0
PGA,0.0
計測震度相当値,0.0
温度フラグ,0
相対湿度フラグ,0
照度フラグ,0
気圧フラグ,0
騒音フラグ,0
eTVOCフラグ,0
eCO2フラグ,0
不快指数フラグ,0
熱中症危険度フラグ,0
SI値フラグ,0
PGAフラグ,0
計測震度相当値フラグ,0

参考文献

  1. 環境センサをカスタマイズする | Pi Zero Easy IoT
  2. ESP32からBLEでオムロン環境センサ(2JCIE-BL)に繋ぐ #OMRON - Qiita
  3. https://play.google.com/store/apps/details?id=com.macdom.ble.blescanner&hl=ja