1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Mongoose OSでDS18B20のmJSライブラリを作ったが…

Last updated at Posted at 2019-10-19

はじめに

Mongoose OSは組み込み向けOSのひとつで、mJSというライブラリによってマイコン上でJavascriptのようなコードを動かすことが可能です。
参考: ESP32をWiFi経由でコントロールするアーキテクチャとライブラリをまとめた

ただ、センサー類のドライバをすべてJSで書くとなると大変なので、
C言語でドライバを書いて、それをJSから利用することが可能な仕組みが用意されています。

今回はC言語でDS18B20の制御コードを書き、それをJSから利用しようとした記録です。

環境

  • Windows 10
  • Mongoose OS v2.1.5
  • ESP32-DEVKITC

結論

思いの外面倒だったので、これなら全部Cで書いたほうが良いと思いました。

Cで書いたドライバ

以下のリポジトリにアップしています。
https://github.com/uhey22e/mongoose-os-ds18b20

以下リポジトリをフォークさせていただきました。
https://github.com/maclema/mongoose-os-ds18b20

mJS用のコードを追加している他に、Initialize/Deinitializeの関数を分離したりなどしています。
mJSに関係ない部分は割愛します。

このライブラリは、Cであれば以下のように使うことが可能です。

main.c
enum mgos_app_init_result mgos_app_init(void) {
    ds18b20_init(25, 10);
    mgos_set_timer(1000 /* ms */, MGOS_TIMER_REPEAT, timer_cb, NULL);
    return MGOS_APP_INIT_SUCCESS;
}

// 1秒おきにOSから呼ばれる
static void timer_cb(void *arg)
{
  // 計測開始
  ds18b20_read_all(temperatures_cb);
  // 諸々の処理...
}


// 計測完了コールバック
static void temperatures_cb(ds18b20_result *results)
{
    if ( results == NULL ) {
        LOG(LL_INFO, ("Results is null."));
    }
    
    // 各センサの計測結果を出力
    while ( results != NULL ) {
        LOG(LL_INFO,
            ("ROM: %s, Temp: %f", results->mac, results->temp));
        results = results->next;
    }
}

結果が入る構造体は以下のようになっています。

ds18b20.h
typedef struct ds18b20_result {
    uint8_t rom[8];                     // センサのアドレス
    char* mac;                          // センサのアドレスを文字列化したもの
    float temp;                         // 計測結果
    struct ds18b20_result* next;        // 次の結果
} ds18b20_result;

DS18B20はひとつのバスに複数のセンサを接続可能です。
そのため、センサがいくつ繋がっていても対応出来るように、Linked Listの形を取っています。

mJSから使う

このドライバを、以下のようなコードで使えるといいなと思っていましたが、そんな簡単にはいきませんでした。

// 動かないコード
load('api_ds18b20.js');

DS18B20.init(25, 10);

DS18B20.readAll(function (results) {
    results.forEach(function (result) {
        print("Addr:", result.mac);
        print("Temp:", result.temp);
    });
}, null);

mJSではJavascriptと比べて色々と制約があります。
https://github.com/cesanta/mjs#Restrictions

最終的なコード

init.js
load('api_ds18b20.js');

DS18B20.init(25, 10);

// 2秒おきに計測
Timer.set(2000, Timer.REPEAT, function () {
  Log.info("Timer Callback");
  DS18B20.read_all();
  // 計測完了まで100ms待つ
  Timer.set(100, false, function () {
    let index = 0;
    // 各センサの計測結果を取得する
    while (true) {
      let result = DS18B20.getResultByIdx(index);
      if (!result) {
        break;
      }
      print(result.mac);
      print(result.temp);
      index++;
    }
  }, null);
}, null);

本当は、read_allのコールバック内で計測結果を取得したかったのですが、
CのコードからmJSのfunctionを呼び出すと、Guru Mediation Errorが発生してしまい、これが解決できませんでした。
結果として、Timerで固定時間のwaitをいれてから、ひとつずつ取得する形式にしました。

mJSとCをつなげるコードは以下のように書きました。

api_ds18b20.js
let DS18B20 = {
  init: ffi('void ds18b20_init(int, int)'),
  deinit: ffi('void ds18b20_deinit(void)'),
  read_all: ffi('void ds18b20_read_all_wrap(void)'),
  _getResultByIdx: ffi('void * get_result_by_idx(int)'),

  // Cの構造体をmJSのオブジェクトに変換
  getResultByIdx: function (idx) {
    let sd = ffi('void * get_ds18b20_result_descr()')();
    let s = this._getResultByIdx(idx);
    if (s !== null) {
      return s2o(s, sd);
    }
    return null;
  },
};

read_all_wrapというmJS用にread_allをラップした関数を作成しました。
他にも、Cの構造体とmJSのオブジェクトを結びつけるためのds18b20_result_descrという定数も作っています。
LinkedListに使うnextのフィールドは、mJSでうまく扱えないため落としています。
アドレスを示すromという配列も、mJSで配列がうまく扱えないため落としています。

ds18b20.c
// For mJS API
static const struct mjs_c_struct_member ds18b20_result_descr[] = {
    {"mac", offsetof(ds18b20_result, mac), MJS_STRUCT_FIELD_TYPE_CHAR_PTR, NULL},
    {"temp", offsetof(ds18b20_result, temp), MJS_STRUCT_FIELD_TYPE_FLOAT, NULL},
    {NULL, 0, MJS_STRUCT_FIELD_TYPE_INVALID, NULL},
};

const struct mjs_c_struct_member* get_ds18b20_result_descr(void)
{
    return ds18b20_result_descr;
}

void ds18b20_read_all_wrap(void)
{
    ds18b20_read_all(NULL);
}

void * get_result_by_idx(int idx)
{
    ds18b20_result* temp;

    // Get result ptr by idx
    temp = _list;
    for (int i = 0; i < idx; i++) {
        if (temp == NULL) {
            return NULL;
        }
        temp = temp->next;
    }
    return temp;
}

まとめ

なんとか、mJSからDS18B20を使えるようにはなりましたが、あまり使い勝手の良いAPIとは言えない状態です。
とはいえ、Guru Mediation Error (LoadProhibited)のデバッグがしんどく、これ以上手を加える気にもなれません。
Cのデータ構造をそのまま持って来られるわけでは無いのも、なかなか面倒でした。

各クラウドへの接続が簡単に出来て、RPCの仕組みも強力なMongoose OS、
メインの制御ロジックにmJSを使えばかなりの省力化が可能でとても良いのですが、
私は「やっぱりCでいいか…」となってしまいました。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?