超精密温度計の製作⑤7セグLEDに表示
ここまでの実験で、小数点第4位まで表示しました。室温ならば、25.1234のような桁数です。これを7セグメントLEDで表示するには、6桁分必要です。しかし、市販品には4桁か8桁がほとんどです。秋月電子通商の74HC595を使った7セグメント表示器は、1桁ずつ並べて、任意の桁が構成できます。
ESP32は3.3Vで動作するので、青色もありますが、順方向電圧の低い赤色と緑色のLEDが実装されて製品を入手しました。使ってみると、緑色でも明るすぎるほどでした。
●接続
SPIのタイミングで74HC595は利用できます。LATCHはCEもしくはSS(5番)と同じタイミングです。SCK(18番)はクロックで、データを送るSDIはMOSI(23番)につなぎます。
ESP32もMOSIから最初のLEDのSDIにデータを入れ、SDOからそのままデータを次のLEDのSDIへ送ります。これを6個分繰り返します。
LATCH、SCK、GND、3.3Vはそれぞれ全部共通につなぎます。
こちらの資料が詳しいです。
●単独でテストするスケッチ
表示できるかをテストします。
#include <SPI.h>
const byte sck = 18; // CLK
const byte latch = 5; // CE
const byte sdi = 23; // MOSI
const byte digits[] = {
0b11111100, // 0
0b01100000, // 1
0b11011010, // 2
0b11110010, // 3
0b01100110, // 4
0b10110110, // 5
0b10111110, // 6
0b11100000, // 7
0b11111110, // 8
0b11110110, // 9
};
const byte dot = 0b00000001;
const byte blank = 0b00000000;
const byte minus = 0b00000010; // -
void setup() {
pinMode(latch, OUTPUT);
digitalWrite(latch, 1);
pinMode(sck, OUTPUT);
pinMode(sdi, OUTPUT);
SPI.begin();
SPI.setBitOrder(LSBFIRST);
SPI.setDataMode(0);
Serial.begin(9600);
Serial.println("Starting");
}
void loop() {
float Temp=0.1237;
// Serial.println(Temp,4);
String sTemp = String(round(Temp*10000) / 10000, DEC);
// Serial.println(sTemp);
int seisuu = sTemp.indexOf(".");
// Serial.println(seisuu);
digitalWrite(latch, 0);
if (Temp > 0.0) {
if (seisuu == 2) { // 23.4567
SPI.transfer(digits[String(sTemp.charAt(0)).toInt()]);
SPI.transfer(digits[String(sTemp.charAt(1)).toInt()] ^ dot);
SPI.transfer(digits[String(sTemp.charAt(3)).toInt()]);
SPI.transfer(digits[String(sTemp.charAt(4)).toInt()]);
SPI.transfer(digits[String(sTemp.charAt(5)).toInt()]);
SPI.transfer(digits[String(sTemp.charAt(6)).toInt()]);
}
if (seisuu == 1) { // 1.2345
SPI.transfer(blank); // blank
SPI.transfer(digits[String(sTemp.charAt(0)).toInt()] ^ dot);
SPI.transfer(digits[String(sTemp.charAt(2)).toInt()]);
SPI.transfer(digits[String(sTemp.charAt(3)).toInt()]);
SPI.transfer(digits[String(sTemp.charAt(4)).toInt()]);
SPI.transfer(digits[String(sTemp.charAt(5)).toInt()]);
}
}else{ // minus
if (seisuu == 3) { // -12.3456
SPI.transfer(minus); // -
SPI.transfer(digits[String(sTemp.charAt(1)).toInt()]);
SPI.transfer(digits[String(sTemp.charAt(2)).toInt()] ^ dot);
SPI.transfer(digits[String(sTemp.charAt(4)).toInt()]);
SPI.transfer(digits[String(sTemp.charAt(5)).toInt()]);
SPI.transfer(digits[String(sTemp.charAt(6)).toInt()]);
}
if (seisuu == 2) { // -1.2222
SPI.transfer(blank); // blank
SPI.transfer(minus); // -
SPI.transfer(digits[String(sTemp.charAt(1)).toInt()] ^ dot);
SPI.transfer(digits[String(sTemp.charAt(3)).toInt()]);
SPI.transfer(digits[String(sTemp.charAt(4)).toInt()]);
SPI.transfer(digits[String(sTemp.charAt(5)).toInt()]);
SPI.transfer(digits[String(sTemp.charAt(6)).toInt()]);
}
}
digitalWrite(latch, 1);
delay(5000);
}
読み取った温度データTempを、零下かどうかで判断し、さらに整数部分が2桁か1桁かで表示を別個に用意しました。
USB電流計で測ると、定常状態で10mAぐらい、適当な数字を表示したとき70~80mAぐらい、全部点灯したとき100~120mAでした。
●全体のスケッチ
前回まで、TMP117bとBLETMP117のタブがありました。これに7segという名称でタブを追加します。三つで一つのスケッチになります。includeや変数の宣言はどこにあっても共通になります。関数はどこからでも呼べます。setup()とloop()は1か所だけです。
●TMP117bのスケッチ
前回から変更名はないです。
#include <Wire.h>
#define TMP117address 0x48
#define TemperatureRegiste 0x00
#define ConfigurationRegister 0x01
float read_tempdata() {
Wire.beginTransmission(TMP117address);
Wire.write((byte)TemperatureRegiste);
Wire.endTransmission();
Wire.requestFrom(TMP117address, 2);
//wait for response
while(Wire.available() == 0);
int T = Wire.read();
T = T << 8 | Wire.read() ;
return ( -(T & 0b1000000000000000) | (T & 0b0111111111111111) ) * 7.8125 /1000.0;
}
●7segのスケッチ
最初の表示テスト用スケッチから、setup()を取り去りました。この中身はBLETMP117のsetup()に移します。また、loop()を関数disp7segLED(float temp)に名称を変更しました。
#include <SPI.h>
const byte sck = 18; st byte latch = 5; // CE
const byte sdi = 23; // MOSI
const byte digits[] = {
0b11111100, // 0
0b01100000, // 1
0b11011010, // 2
0b11110010, // 3
0b01100110, // 4
0b10110110, // 5
0b10111110, // 6
0b11100000, // 7
0b11111110, // 8
0b11110110, // 9
};
const byte dot = 0b00000001;
const byte blank = 0b00000000;
const byte minus = 0b00000010; // -
float temp;
void disp7segLED(float temp) {
float Temp=temp;
String sTemp = String(round(Temp*10000) / 10000, DEC);
int seisuu = sTemp.indexOf(".");
digitalWrite(latch, 0);
if (Temp > 0.0) {
if (seisuu == 2) { // 23.4567
SPI.transfer(digits[String(sTemp.charAt(0)).toInt()]);
SPI.transfer(digits[String(sTemp.charAt(1)).toInt()] ^ dot);
SPI.transfer(digits[String(sTemp.charAt(3)).toInt()]);
SPI.transfer(digits[String(sTemp.charAt(4)).toInt()]);
SPI.transfer(digits[String(sTemp.charAt(5)).toInt()]);
SPI.transfer(digits[String(sTemp.charAt(6)).toInt()]);
}
if (seisuu == 1) { // 1.2345
SPI.transfer(blank); // blank
SPI.transfer(digits[String(sTemp.charAt(0)).toInt()] ^ dot);
SPI.transfer(digits[String(sTemp.charAt(2)).toInt()]);
SPI.transfer(digits[String(sTemp.charAt(3)).toInt()]);
SPI.transfer(digits[String(sTemp.charAt(4)).toInt()]);
SPI.transfer(digits[String(sTemp.charAt(5)).toInt()]);
}
}else{ // minus
if (seisuu == 3) { // -12.3456
SPI.transfer(minus); // -
SPI.transfer(digits[String(sTemp.charAt(1)).toInt()]);
SPI.transfer(digits[String(sTemp.charAt(2)).toInt()] ^ dot);
SPI.transfer(digits[String(sTemp.charAt(4)).toInt()]);
SPI.transfer(digits[String(sTemp.charAt(5)).toInt()]);
SPI.transfer(digits[String(sTemp.charAt(6)).toInt()]);
}
if (seisuu == 2) { // -1.2222
SPI.transfer(blank); // blank
SPI.transfer(minus); // -
SPI.transfer(digits[String(sTemp.charAt(1)).toInt()] ^ dot);
SPI.transfer(digits[String(sTemp.charAt(3)).toInt()]);
SPI.transfer(digits[String(sTemp.charAt(4)).toInt()]);
SPI.transfer(digits[String(sTemp.charAt(5)).toInt()]);
SPI.transfer(digits[String(sTemp.charAt(6)).toInt()]);
}
}
digitalWrite(latch, 1);
}
●BLETMP117のスケッチ
setup()が少し膨らみました。loop()から上記のdisp7segLED(float temp)関数を呼びます。
Wire.write((byte)0x02); // high とWire.write(0x20); // lowは、測定する温度の傾向によって、更新頻度や平均化する測定数を変更してください。連載の1回目に大まかな説明があります。
/*
Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleServer.cpp
Ported to Arduino ESP32 by Evandro Copercini
updates by chegewara
*/
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>
// See the following for generating UUIDs:
// https://www.uuidgenerator.net/
#define SERVICE_UUID "1c3b07d0-7106-4114-add9-43bd176d104e"
#define CHARACTERISTIC_UUID "e819baa6-6d98-4090-937b-88dd2ca0a7da"
BLECharacteristic *pCharacteristic;
void setup() {
Wire.begin();
Wire.setClock(400000);
Wire.beginTransmission(TMP117address);
Wire.write((byte)ConfigurationRegister);
Wire.write((byte)0x02); // high
Wire.write(0x20); // low
Wire.endTransmission();
Serial.begin(9600);
Serial.println("Starting BLE work!");
pinMode(latch, OUTPUT);
digitalWrite(latch, 1);
pinMode(sck, OUTPUT);
pinMode(sdi, OUTPUT);
SPI.begin();
SPI.setBitOrder(LSBFIRST);
SPI.setDataMode(0);
Serial.println("7segLED ready");
BLEDevice::init("ESP32-TMP117 peripheral");
BLEServer *pServer = BLEDevice::createServer();
BLEService *pService = pServer->createService(SERVICE_UUID);
pCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID, BLECharacteristic::PROPERTY_READ);
float temp = read_tempdata();
Serial.println("temp sensor ready");
pCharacteristic->setValue((uint8_t*)&temp,1);
pService->start();
// BLEAdvertising *pAdvertising = pServer->getAdvertising(); // this still is working for backward compatibility
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
pAdvertising->addServiceUUID(SERVICE_UUID);
pAdvertising->setScanResponse(true);
pAdvertising->setMinPreferred(0x06); // functions that help with iPhone connections issue
pAdvertising->setMinPreferred(0x12);
BLEDevice::startAdvertising();
}
void loop() {
temp = read_tempdata();
Serial.println(temp,4);
pCharacteristic->setValue(temp);
pCharacteristic->notify();
disp7segLED(temp);
delay(2000);
}
実行例です。