#はじめに
この記事では,TOPPERS/R2CAによる NCES IoT Base Shield のGrove機能の+RTOS機能使い方について説明します.
NCES IoT Base Shiledについてはこの記事を参照して下さい.
NCES IoT Base ShiledでGroveモジュールを使用する方法についてはこの記事を参照して下さい.
必要なハードウェア
-
NCES IoT Base Shield
-
Windows PC
-
Windows7とWindows10で動作を確認しています.
-
USB(microB)ケーブル
-
ボードには付属していないので用意して下さい.
アプリケーション仕様
作成するアプリケーションの仕様は次の通り.
- 機能1:LEDを1秒周期で点滅する
- 機能2:周期的にDigital Light センサーの値を読み込んで,値をOLED Display に表示する.
- 機能3:Touch Sensorを押している間は,Light センサーの値の表示の更新を停止して,OLED Displayの表示を反転させる.
- 機能4:Chainable LEDを周期的に色を変更する.
ハードウェアセットアップ
各種センサーをIoTシールドに接続します.Grove Digital LightとGrove OLED Displayは,I2Cポートに接続します.Touch SensorはD4以外のデジタルのコネクタに接続しますここではD3に接続します.LEDはD4に,Chainable RGB LEDはD8に接続します
プロジェクトフォルダ
プロジェクトフォルダは,NCESIoT_RTOS にあります.必要なライブラリ等は入っていますので,ハードウェアの
セットアップ後に動作させることは可能です.
割込み無し版
まず割込みを使用しないバージョンを作成します.
各モジュールのライブラリはGroveのWikiからダウンロードしてプロジェクトフォルダ以下に置きます.今回はDigital Light,OLED Display,ChainableLEDのライブラリを使用します.
まず機能1-4を別々のタスクで実現するためにタスクを追加で3個(合計4個)作成します.
#define R2CA_NUM_TASK 3
割込みを使わないのでUSE_INTERRUPTをコメントアウトします.ifdefが入っていると読みにくいのでifdefをとった状態のコードを掲載します.
#include "r2ca.h"
#include <Wire.h>
#include <Digital_Light_TSL2561.h>
#include <SeeedOLED.h>
#include <ChainableLED.h>
#define LED_PIN 4
extern void task1_setup();
extern void task2_setup();
extern void task3_setup();
void setup()
{
Serial.begin(115200);
pinMode(LED_PIN, OUTPUT);
task1_setup();
task2_setup();
task3_setup();
}
void loop()
{
digitalWrite(LED_PIN, HIGH); // turn the LED on (HIGH is the voltage level)
delay(1000); // wait for a second
digitalWrite(LED_PIN, LOW); // turn the LED off by making the voltage LOW
delay(1000); // wait for a second
}
#define TOUCH_PIN 3
int is_update_oled;
void task1_setup()
{
Wire.begin();
TSL2561.init();
SeeedOled.init();
SeeedOled.deactivateScroll();
is_update_oled = 1;
}
void task1_loop()
{
Serial.print("The Light value is: ");
Serial.println(TSL2561.readVisibleLux());
if (is_update_oled == 1) {
wai_sem(OLED_SEM);
SeeedOled.clearDisplay();
SeeedOled.putNumber(TSL2561.readVisibleLux());
sig_sem(OLED_SEM);
}
delay(1000);
}
void task2_setup()
{
pinMode(TOUCH_PIN, INPUT_PULLUP);
}
void task2_loop()
{
delay(1);
int TouchSensorValue = digitalRead(TOUCH_PIN);
if(TouchSensorValue==1) {
is_update_oled = 0;
wai_sem(OLED_SEM);
SeeedOled.setInverseDisplay();
sig_sem(OLED_SEM);
}else{
is_update_oled = 1;
wai_sem(OLED_SEM);
SeeedOled.setNormalDisplay();
sig_sem(OLED_SEM);
}
}
#define NUM_LEDS 1
ChainableLED leds(8, 9, NUM_LEDS);
void task3_setup()
{
leds.init();
}
float hue = 0.0;
boolean up = true;
int count = 0;
void task3_loop()
{
for (byte i=0; i<NUM_LEDS; i++)
leds.setColorHSB(i, hue, 1.0, 0.5);
delay(50);
if (up)
hue+= 0.025;
else
hue-= 0.025;
if (hue>=1.0 && up)
up = false;
else if (hue<=0.0 && !up)
up = true;
}
メインタスクセットアップ(setup())はシリアルポートとLEDのための初期化を行っています.
メインタスクループ(loop())では,LEDを一秒間隔で点滅させます.
タスク1セットアップ(task1_setup())では, Digital Light と OLED Display を初期化しています.
タスク1ループ(task1_loop())では,1秒ごとにDigital Light の値を読み込んでその結果をOLEDに表示します.
タスク2セットアップ(task2_setup())では,Touch Sensorのためにポートを初期化します.Touch Sensorを接続しているポートはマクロTOUCH_PINで定義しています.
タスク2ループ(task2_loop())では,Touch Sensorを読み込んで,押されていればOLEDを反転させます.ここで重要なのは,delay(1)を入れることです.これを入れないとメインタスクに処理が移りません.
タスク間の通信はグローバル変数で実現しています.具体的には,is_update_oledを使用しています.Touch Sensorが押されていれば is_update_oled を0に設定します.is_update_oled が0だとメインタスクはOLEDの更新を停止します.
OLEDは両方のタスクから操作するため,セマフォにより排他制御します.操作前にwai_sem()でセマフォを取得して,使用後にsig_sem()でセマフォを返却します.
セマフォはr2ca_app.cfgに宣言します.
INCLUDE("r2ca_lib.cfg");
CRE_SEM(OLED_SEM, {TA_NULL, 1, 1});
タスク3セットアップ(task3_setup())では,ChainableLEDのための初期化を行います.
タスク2ループ(task3_loop())では,ChainableLEDの色を周期的に変更します.
割込みあり版
次に割込みを使用するバージョンを作成します.
USE_INTERRUPTの定義を有効にします.ifdefが入っていると読みにくいのでifdefをとった状態のコードを掲載します.
#include "r2ca.h"
#include <Wire.h>
#include <Digital_Light_TSL2561.h>
#include <SeeedOLED.h>
#include <ChainableLED.h>
#define LED_PIN 4
void task1_setup();
void task2_setup();
void task3_setup();
void setup()
{
Serial.begin(115200);
pinMode(LED_PIN, OUTPUT);
task1_setup();
task2_setup();
task3_setup();
}
void loop()
{
digitalWrite(LED_PIN, HIGH); // turn the LED on (HIGH is the voltage level)
delay(1000); // wait for a second
digitalWrite(LED_PIN, LOW); // turn the LED off by making the voltage LOW
delay(1000); // wait for a second
}
#define TOUCH_PIN 3
int is_update_oled;
void task1_setup()
{
Wire.begin();
TSL2561.init();
SeeedOled.init();
SeeedOled.deactivateScroll();
is_update_oled = 1;
}
void task1_loop()
{
Serial.print("The Light value is: ");
Serial.println(TSL2561.readVisibleLux());
if (is_update_oled == 1) {
wai_sem(OLED_SEM);
SeeedOled.clearDisplay();
SeeedOled.putNumber(TSL2561.readVisibleLux());
sig_sem(OLED_SEM);
}
delay(1000);
}
void onTouch(void) {
iwup_tsk(R2CA_TASK1);
}
void task2_setup()
{
pinMode(TOUCH_PIN, INPUT_PULLUP);
attachInterrupt(TOUCH_PIN, onTouch, CHANGE);
}
void task2_loop()
{
slp_tsk();
int TouchSensorValue = digitalRead(TOUCH_PIN);
if(TouchSensorValue==1) {
is_update_oled = 0;
wai_sem(OLED_SEM);
SeeedOled.setInverseDisplay();
sig_sem(OLED_SEM);
}else{
is_update_oled = 1;
wai_sem(OLED_SEM);
SeeedOled.setNormalDisplay();
sig_sem(OLED_SEM);
}
}
#define NUM_LEDS 1
ChainableLED leds(8, 9, NUM_LEDS);
void task3_setup()
{
leds.init();
}
float hue = 0.0;
boolean up = true;
int count = 0;
void task3_loop()
{
for (byte i=0; i<NUM_LEDS; i++)
leds.setColorHSB(i, hue, 1.0, 0.5);
delay(50);
if (up)
hue+= 0.025;
else
hue-= 0.025;
if (hue>=1.0 && up)
up = false;
else if (hue<=0.0 && !up)
up = true;
}
割込み無し版との差分について説明します.
割込みあり版では,Touch Sensor の状態が変化すると割込みハンドラを起動するようにします.
まずタスク2セットアップ(task2_setup())で,attachInterrupt()を呼び出して割込みを設定します.Touch Sensor を押した際にも話した際にも割込みを発生させたいため,引数にCHANGEを指定します.
割込みハンドラは onTouch()として宣言します.onTouch()は,iwup_tsk()でタスク1を起動します.
タスク2メインループ(task2_loop())ではonTouch()からiwup_tsk()により起動されるのを待ち,起動されたらTouch Sensor の値を読み,割込みなし版と同じ処理を実施します.
おわりに
この記事では,RTOSのAPIの実例を紹介しました.