C++ の勉強 ⑦ まとめ。sht45の測定結果の温度と湿度を7セグメントLEDに表示する

　前回のプログラムでは、二つの７セグメントLEDを利用できるには中途半端になっていました。最初に、二つとも表示ができるように修正します。
 　具体的には、アドレスを引数に追加しました。
 　その後、sht45その湿度と温度を表示します。
  
 ```ex129.cpp
#include <iostream>
#include <string>
#include <iomanip>
#include <map>
#include <cstdint>
#include <array>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/i2c-dev.h>
#include <linux/i2c.h>
#include <unistd.h>
#include <string>

#define white_ht16k33Adr 0x71
#define red_ht16k33Adr 0x70
#define HT16K33_GENERIC_SYSTEM_ON 0x21
#define HT16K33_GENERIC_DISPLAY_ON 0x81
#define HT16K33_GENERIC_CMD_BRIGHTNESS 0xe0

int fd;
uint8_t address, data, segment, resister, level;
int systemOn(uint8_t address);
int displayOn(uint8_t address);
int brightnessSet(uint8_t address,int level);
int one_byte_write(uint8_t address, uint8_t resisterAdr, uint8_t setData);
int clearDisplay(uint8_t address);
int display(uint8_t address, uint8_t dispSegment, uint8_t dispData);
int s, i;
int convertDispData(uint8_t address, float measurementData);
std::array<uint8_t, 4> segmentDispData = {0, 0, 0, 0};

int main(){
    std::cout << "start display HT16K33\n";
    fd = open("/dev/i2c-1", O_RDWR);
    systemOn(white_ht16k33Adr); systemOn(red_ht16k33Adr);
    usleep(1000);
    displayOn(white_ht16k33Adr); displayOn(red_ht16k33Adr);
    usleep(1000);
    brightnessSet(white_ht16k33Adr, 8); brightnessSet(red_ht16k33Adr, 8);
    usleep(1000);
    clearDisplay(white_ht16k33Adr); clearDisplay(red_ht16k33Adr);
    usleep(1000);

    float temp = -9.06;
    convertDispData(white_ht16k33Adr, temp);
    float humi = 98;
    convertDispData(red_ht16k33Adr, humi);

    return 0;
}

int convertDispData(uint8_t address, float measurementData){
    std::array<uint8_t, 4> segmentDispData;
    std::string measurementDataString = std::to_string(measurementData);
    std::string measurementDataStringSlim = measurementDataString.erase(measurementDataString.find("00"));
    std::cout << "input measurementData(string): " << measurementDataStringSlim << "\n";
    int size = measurementDataStringSlim.size();

    std::map<unsigned char, uint8_t> figData{
            {'0', 0x3f}, {'1', 0x06}, {'2', 0x5b}, {'3', 0x4f}, {'4', 0x66}, {'5', 0x6d},
            {'6', 0x7d}, {'7', 0x07}, {'8', 0x7f}, {'9', 0x67}, {'-', 0x40} ,{'.', 0x80}};

    std::map<uint8_t, uint8_t> segmentData{{1, 0}, {2, 2}, {3, 6}, {4, 8}};

    s = 4;
    for (i = 1; i < size + 1; i++){
        if (measurementDataStringSlim[size-i] == '.'){
            i++;
            segmentDispData[s] = figData.at(measurementDataStringSlim[size-i]) | 0x80;
        }else{
            segmentDispData[s] = figData.at(measurementDataStringSlim[size-i]);
        }
    std::cout << "segment" << s << ": " <<  measurementDataString[size-i]
              << " 0x" << std::hex << (int)segmentDispData[s] << "\n";

    display(address, segmentData[s], (int)segmentDispData[s]);
    s--;
    }
     return 0;
}

int systemOn(uint8_t address){
    one_byte_write(address, HT16K33_GENERIC_SYSTEM_ON, 0);
    return 0;
}

int displayOn(uint8_t address){
    one_byte_write(address, HT16K33_GENERIC_DISPLAY_ON, 0);
    return 0;
}

int brightnessSet(uint8_t address, int level){
    one_byte_write(address, HT16K33_GENERIC_CMD_BRIGHTNESS, level);
    return 0;
}

int one_byte_write(uint8_t address, uint8_t resisterAdr, uint8_t setData){
    struct i2c_msg msg[1];  // /usr/include/linux/i2c.h
    struct i2c_rdwr_ioctl_data packets;  // /usr/include/linux/i2c-dev.h
    uint8_t data[2];

    msg[0].addr =  address;  // device address
    msg[0].flags = 0;   // read、writeやアドレス長の指定
    msg[0].len = 1;     // bufに指定するdataのサイズ
    msg[0].buf = data;

    data[0] = resisterAdr;
    data[1] = setData;
    packets.msgs = msg;
    packets.nmsgs = 1;  //  msgのサイズ指定

    int ret = ioctl(fd, I2C_RDWR, &packets);

    return 0;
}

int clearDisplay(uint8_t address){
    struct i2c_msg msg[1];
    struct i2c_rdwr_ioctl_data packets;
    uint8_t data[11];

    msg[0].addr = address;  // device address
    msg[0].flags = 0;    // read、writeやアドレス長の指定
    msg[0].len = 11;     // bufに指定するdataのサイズ
    msg[0].buf = data;

    data[0] = 0;
    data[1] = 0x00;
    data[2] = 0x00;
    data[3] = 0x00;
    data[4] = 0x00;
    data[5] = 0x00;
    data[6] = 0x00;
    data[7] = 0x00;
    data[8] = 0x00;
    data[9] = 0x00;
    data[10] = 0x00;
    packets.msgs = msg;
    packets.nmsgs = 1;  //  msgのサイズ指定

    int retclear = ioctl(fd, I2C_RDWR, &packets);

    return 0;
}

int display(uint8_t address, uint8_t dispSegment, uint8_t dispData){
    struct i2c_msg msgP[1];
    struct i2c_rdwr_ioctl_data packetsP;
    unsigned char data[2];

    msgP[0].addr =  address;
    msgP[0].flags = 0;
    msgP[0].len = 2;
    msgP[0].buf = data;

    data[0] = dispSegment;
    data[1] = dispData;
    packetsP.msgs = msgP;
    packetsP.nmsgs = 1;        // データ書き込みでmsgは1
    int retP = ioctl(fd, I2C_RDWR, &packetsP);

    return 0;
}
```
　実行します。赤色と白色の7セグメントLEDの両方を制御できるようになりました。
 ```
 $ g++ ex129.cpp
yoshi@yoshi:~/book$ ./a.out
start display HT16K33
input measurementData(string): -9.06
segment4: 6 0x7d
segment3: 0 0x3f
segment2: 9 0xe7
segment1: - 0x40
input measurementData(string): 98.
segment4: 8 0xff
segment3: 9 0x67
```

<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407635/d076b77c-1121-f5ae-6aa1-3eaee9fa27f4.png">


### sht45の測定結果を表示する
　sht45の測定結果である温度と湿度を、7セグメントLEDに表示します。
 　たくさんあった、初期設定類は、``init_ht16k33()``と``init_sht45()``に集約しました。


```ex130.cpp
#include <iostream>
#include <string>
#include <iomanip>
#include <map>
#include <cstdint>
#include <array>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/i2c-dev.h>
#include <linux/i2c.h>
#include <unistd.h>
#include <string>
#include <tuple>

#define white_ht16k33Adr 0x71
#define red_ht16k33Adr 0x70
#define HT16K33_GENERIC_SYSTEM_ON 0x21
#define HT16K33_GENERIC_DISPLAY_ON 0x81
#define HT16K33_GENERIC_CMD_BRIGHTNESS 0xe0
#define sht45Adr 0x44

int fd;
uint8_t address, data, segment, resister, level, i, s;
int systemOn(uint8_t address);
int displayOn(uint8_t address);
int brightnessSet(uint8_t address,int level);
int one_byte_write(uint8_t address, uint8_t resisterAdr, uint8_t setData);
int one_byte_write_noaddress(uint8_t address, uint8_t setData);
int clearDisplay(uint8_t address);
int display(uint8_t address, uint8_t dispSegment, uint8_t dispData);
int convertDispData(uint8_t address, float measurementData);
std::array<uint8_t, 4> segmentDispData = {0, 0, 0, 0};
std::tuple<float, float> readSht45_data();
uint8_t CalcCrc(const uint8_t * crcdata, uint8_t size);
uint8_t reflect(uint8_t c);

float temp;
float humi;
int init_sht45();
int init_ht16k33();

int main(){
    std::cout << "start display HT16K33\n";
    fd = open("/dev/i2c-1", O_RDWR);
    init_ht16k33();
    init_sht45();

    auto [temp, humi] = readSht45_data();
    std::cout << "\ntemp is "  << std::fixed << std::setprecision(2) << temp << "`C\n";
    std::cout << "humi is "  << std::fixed << std::setprecision(1) << humi << "%\n";
    int temp_10 = int(10.0 * temp); temp = float(temp_10) /10.0;
    int humi_1 = int(humi); humi = float(humi_1) /1.0;
    convertDispData(white_ht16k33Adr, temp);
    convertDispData(red_ht16k33Adr, humi);

 return 0;
}

int init_ht16k33(){
    systemOn(white_ht16k33Adr); systemOn(red_ht16k33Adr);
    usleep(1000);
    displayOn(white_ht16k33Adr); displayOn(red_ht16k33Adr);
    usleep(1000);
    brightnessSet(white_ht16k33Adr, 8); brightnessSet(red_ht16k33Adr, 8);
    usleep(1000);
    clearDisplay(white_ht16k33Adr); clearDisplay(red_ht16k33Adr);
    usleep(1000);
    std::cout << "\ninit ht16k33  \n";
    return 0;
}

int init_sht45(){
    one_byte_write_noaddress(sht45Adr,0x94);  // reset
    usleep(1000);
    one_byte_write_noaddress(sht45Adr,0xfd);  // high precision
    usleep(10000);
    std::cout << "\ninit sht45  \n";
    return 0;
}

int convertDispData(uint8_t address, float measurementData){
    std::string measurementDataString;
    std::string measurementDataStringSlim;
    std::cout << "input measurementData(float): "<< std::fixed << std::setprecision(3) << measurementData << "\n";
    std::array<uint8_t, 4> segmentDispData;
    measurementDataString = std::to_string(measurementData);
    std::cout << "measurementDataString (to_string) " << measurementDataString << "\n";
    if (measurementDataString.find("00") != std::string::npos){
        std::cout << "00\n";
        measurementDataStringSlim = measurementDataString.erase(measurementDataString.find("00"));
    }

    if (measurementDataString.find("99") != std::string::npos){
        std::cout << "99\n";
        measurementDataStringSlim = measurementDataString.erase(measurementDataString.find("99"));
    }
        std::cout << "input measurementData(string): " << measurementDataStringSlim << "\n";
        int size = measurementDataStringSlim.size();
//        std::cout << "measurementData(string) length" << size << "\n";

    std::map<unsigned char, uint8_t> figData{
            {'0', 0x3f}, {'1', 0x06}, {'2', 0x5b}, {'3', 0x4f}, {'4', 0x66}, {'5', 0x6d},
            {'6', 0x7d}, {'7', 0x07}, {'8', 0x7f}, {'9', 0x67}, {'-', 0x40} ,{'.', 0x80}};

    std::map<uint8_t, uint8_t> segmentData{{1, 0}, {2, 2}, {3, 6}, {4, 8}};

    s = 4;
    for (i = 1; i < size + 1; i++){
        if (measurementDataStringSlim[size-i] == '.'){
            i++;
            segmentDispData[s] = figData.at(measurementDataStringSlim[size-i]) | 0x80;
        }else{
            segmentDispData[s] = figData.at(measurementDataStringSlim[size-i]);
        }
    std::cout << "segment" << (int)s << ": " <<  measurementDataString[size-i]
              << " 0x" << std::hex << (int)segmentDispData[s] << "\n";

    display(address, segmentData[s], (int)segmentDispData[s]);
    s--;
    }
     return 0;
}

int systemOn(uint8_t address){
    one_byte_write(address, HT16K33_GENERIC_SYSTEM_ON, 0);
    return 0;
}

int displayOn(uint8_t address){
    one_byte_write(address, HT16K33_GENERIC_DISPLAY_ON, 0);
    return 0;
}

int brightnessSet(uint8_t address, int level){
    one_byte_write(address, HT16K33_GENERIC_CMD_BRIGHTNESS, level);
    return 0;
}

int one_byte_write(uint8_t address, uint8_t resisterAdr, uint8_t setData){
    struct i2c_msg msg[1];  // /usr/include/linux/i2c.h
    struct i2c_rdwr_ioctl_data packets;  // /usr/include/linux/i2c-dev.h
    uint8_t data[2];

    msg[0].addr =  address;  // device address
    msg[0].flags = 0;   // read、writeやアドレス長の指定
    msg[0].len = 1;     // bufに指定するdataのサイズ
    msg[0].buf = data;

    data[0] = resisterAdr;
    data[1] = setData;
    packets.msgs = msg;
    packets.nmsgs = 1;  //  msgのサイズ指定

    int ret = ioctl(fd, I2C_RDWR, &packets);

    return 0;
}

int one_byte_write_noaddress(uint8_t address, uint8_t setData){
    struct i2c_msg msg[1];  // /usr/include/linux/i2c.h
    struct i2c_rdwr_ioctl_data packets;  // /usr/include/linux/i2c-dev.h
    uint8_t data[1];

    msg[0].addr =  address;  // device address
    msg[0].flags = 0;   // read、writeやアドレス長の指定
    msg[0].len = 1;     // bufに指定するdataのサイズ
    msg[0].buf = data;

    data[0] = setData;
    packets.msgs = msg;
    packets.nmsgs = 1;  //  msgのサイズ指定

    int ret = ioctl(fd, I2C_RDWR, &packets);

    return 0;
}

int clearDisplay(uint8_t address){
    struct i2c_msg msg[1];
    struct i2c_rdwr_ioctl_data packets;
    uint8_t data[11];

    msg[0].addr = address;  // device address
    msg[0].flags = 0;    // read、writeやアドレス長の指定
    msg[0].len = 11;     // bufに指定するdataのサイズ
    msg[0].buf = data;

    data[0] = 0;
    data[1] = 0x00;
    data[2] = 0x00;
    data[3] = 0x00;
    data[4] = 0x00;
    data[5] = 0x00;
    data[6] = 0x00;
    data[7] = 0x00;
    data[8] = 0x00;
    data[9] = 0x00;
    data[10] = 0x00;
    packets.msgs = msg;
    packets.nmsgs = 1;  //  msgのサイズ指定

    int retclear = ioctl(fd, I2C_RDWR, &packets);

    return 0;
}

int display(uint8_t address, uint8_t dispSegment, uint8_t dispData){
    struct i2c_msg msgP[1];
    struct i2c_rdwr_ioctl_data packetsP;
    unsigned char data[2];

    msgP[0].addr =  address;
    msgP[0].flags = 0;
    msgP[0].len = 2;
    msgP[0].buf = data;

    data[0] = dispSegment;
    data[1] = dispData;
    packetsP.msgs = msgP;
    packetsP.nmsgs = 1;        // データ書き込みでmsgは1
    int retP = ioctl(fd, I2C_RDWR, &packetsP);

    return 0;
}

std::tuple<float, float> readSht45_data(){
    // read 6 data
    struct i2c_msg msgP[1];
    struct i2c_rdwr_ioctl_data packetsP;
    unsigned char data1[1], data2[6];

    msgP[0].addr =  sht45Adr;
    msgP[0].flags = I2C_M_RD;   // アドレスを書き込み
    msgP[0].len = 6;
    msgP[0].buf = &data2[0];

    packetsP.msgs = msgP;
    packetsP.nmsgs = 1;        // データ読み出しでmsgは1
    int retP = ioctl(fd, I2C_RDWR, &packetsP);

//    std::cout << "read all data " << (int)data2[0] << " " << (int)data2[1] << " " << (int)data2[2] << " " << (int)data2[3] << " " << (int)data2[4] << " " << (int)data2[5] << "\n";

    int tempCrc = CalcCrc(data2, 2);
    if (data2[2] == tempCrc) {

//        std::cout << " temp crc is 0x" << std::hex << (int)data2[2] << " calcedCRC is 0x" << std::hex << tempCrc << "\n";

        int Stemp = data2[0] << 8 | data2[1];
        temp = -45 + (float)Stemp * 175.0 / 65535.0;
    }
    int humiCrc = CalcCrc(data2+3, 2);
    if (data2[5] == humiCrc) {

//        std::cout << " humi crc is 0x" << std::hex << (int)data2[5] << " calcedCRC is 0x" << std::hex << humiCrc << "\n";

        int SRH = data2[3] << 8 | data2[4];
        humi = 100.0 * (float)SRH / 65535.0;
    }

    return  std::make_tuple(temp, humi);
}

uint8_t table[256] ={
   0, 94, 188, 226, 97, 63, 221, 131, 194, 156, 126, 32, 163, 253, 31, 65,157, 195, 33, 127, 252, 162, 64, 30, 95, 1, 227, 189, 62, 96, 130, 220,35, 125, 159,193, 66, 28, 254, 160, 225, 191, 93, 3, 128, 222, 60, 98, 190, 224, 2, 92, 223, 129, 99, 61, 124, 34, 192, 158, 29, 67, 161, 255,70, 24, 250, 164, 39, 121, 155, 197, 132, 218, 56, 102, 229, 187, 89, 7, 219, 133, 103, 57, 186, 228, 6, 88, 25, 71, 165, 251, 120, 38, 196, 154, 101, 59, 217, 135, 4, 90, 184, 230, 167, 249, 27, 69, 198, 152, 122, 36,248, 166, 68, 26, 153, 199, 37, 123, 58, 100, 134, 216, 91, 5, 231, 185,140, 210, 48, 110, 237, 179, 81, 15, 78, 16, 242, 172, 47, 113, 147, 205,17, 79, 173, 243, 112, 46, 204, 146, 211, 141, 111, 49, 178, 236, 14, 80,175, 241, 19, 77, 206, 144, 114, 44, 109, 51, 209, 143, 12, 82, 176, 238, 50, 108, 142, 208, 83, 13, 239, 177, 240, 174, 76, 18, 145, 207, 45, 115,202, 148, 118, 40, 171, 245, 23, 73, 8, 86, 180, 234, 105, 55, 213, 139,87, 9, 235, 181, 54, 104, 138, 212, 149, 203, 41, 119, 244, 170, 72, 22,233, 183, 85, 11, 136, 214, 52, 106, 43, 117, 151, 201, 74, 20, 246, 168,116, 42, 200, 150, 21, 75, 169, 247, 182, 232, 10, 84, 215, 137, 107, 53
};

uint8_t CalcCrc(const uint8_t * crcdata, uint8_t length){

//    std::cout << "in function \n new data 0x" << std::hex << (int)crcdata[0] << std::hex << (int)crcdata[1]
//              << " 0b" << ((std::bitset<8>(*crcdata)).to_string()).insert(4, " ")
//              << "\n data size is " << (int)length << "\n";

    uint8_t crc = 0xff;
    crc = table[reflect(crcdata[0]) ^ crc];
    crc = table[reflect(crcdata[1]) ^ crc];

    return reflect(crc);
}

uint8_t reflect( uint8_t c ){
  c = ((c & 0x55) <<  1) | (c >>  1) & 0x55;
  c = ((c & 0x33) <<  2) | (c >>  2) & 0x33;
  c = ((c & 0x0f) <<  4) | (c >>  4) & 0x0f;
  return c;
}
```
　実行します。
 ```
 $ g++ ex130.cpp
yoshi@yoshi:~/book$ ./a.out
start display HT16K33

init ht16k33

init sht45

temp is 27.89`C
humi is 58.6%
input measurementData(float): 27.800
measurementDataString (to_string) 27.799999
99
input measurementData(string): 27.7
segment4: 7 0x7
segment3: 7 0x87
segment2: 2 0x5b
input measurementData(float): 58.000
measurementDataString (to_string) 58.000000
00
input measurementData(string): 58.
segment4: 8 0xff
segment3: 5 0x6d
```

　少し冗長な出力になっています。
 　前回、実数-12.3をto_stringしたら、-12.300000になったという話を書きました。それだけではなかったのです。-12.299999という出力も出るのです。実数は実に恐ろしやです。ほかの事例も出てくるかもしれないので、その経過を出力させています。


<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407635/5da0bb47-b4a4-cd76-0a35-e45bfe39c786.png">

　さらに、計測値が27.89℃なのに、表示は27.7というのもおかしい。変化途中でミスってるような気もします。