This is the multi-page printable view of this section. Click here to print...

Return to the regular view of this page

As of 2025-07-24

Wire (Helper Class Version)

Wire (How to use with helper classes)
    The helper class version is a more abstract implementation. Generating objects reader, writer that correspond to reading and writing marks the start of bus usage, and destroying the object performs the bus usage termination procedure.

    By creating objects inside the condition expression of an if statement, the object’s lifetime is limited to the scope of the if block. When exiting the if block, the object is destroyed, and at that time, the bus usage termination procedure is performed.

    if (auto&& wrt = Wire.get_writer(...)) { // Object creation and device communication check
       // The scope (curly braces) is the lifetime of wrt.
       wrt << 0x00; // Write 0x00 using the mwx::stream interface
    }
    // wrt is destroyed when exiting the if block, and bus termination is performed
    

    Also, since the read/write objects implement the mwx::stream interface, operators like << can be used.

    • Matching the start and end of bus usage with the object’s lifetime improves code readability and prevents omission of termination procedures.
    • Unified reading and writing procedures via the mwx::stream interface.

    Reading

    This is a reading method using helper classes to perform reading and its termination procedure within a scope if() { ... }.

    const uint8_t DEV_ADDR = 0x70;
    uint8_t au8data[6];
    if (auto&& rdr = Wire.get_reader(DEV_ADDR, 6)) {
    		for (auto&& c: au8data) {
    			c = rdr();
    		}
    }
    
    // same above
    uint16_t u16temp, u16humd;
    uint8_t u8temp_csum, u8humd_csum;
    if (auto&& rdr = Wire.get_reader(SHTC3_ADDRESS, 6)) {
    		rdr >> u16temp;
    		rdr >> u8temp_csum;
    		rdr >> u16humd;
    		rdr >> u8humd_csum;
    }

    Above, the rdr object generated by the get_reader() method is used to read one byte at a time. The method parameter specifies the two-wire serial ID to read.

    1. The rdr object is created inside if(...). (The type is resolved as a universal reference auto&& by type inference.)
    2. The created rdr object defines operator bool () and is used for evaluation in the condition. If communication is possible with the specified ID, it returns true.
    3. The rdr object defines int operator () (void), which reads one byte from the two-wire serial bus when called. Returns -1 on failure, or the read byte value on success.
    4. The destructor of rdr is called at the end of the if() { ... } scope, issuing a STOP on the two-wire serial bus.

    get_reader(addr, read_count=0)

    periphe_wire::reader
    get_reader(uint8_t addr, uint8_t read_count = 0)

    Obtains a worker object used for I2C reading.

    ParameterDescription
    addrI2C address for reading
    read_countNumber of bytes to read (if specified, a STOP bit is issued at the end of the transfer). If 0 is specified, no STOP bit is issued (some devices may work without it).

    Writing (writer)

    This is a writing method using helper classes to perform writing and its termination procedure within a scope if() { ... }.

    const uint8_t DEV_ADDR = 0x70;
    if (auto&& wrt = Wire.get_writer(DEV_ADDR)) {
    	wrt(SHTC3_TRIG_H);
    	wrt(SHTC3_TRIG_L);
    }
    
    // same above
    if (auto&& wrt = Wire.get_writer(DEV_ADDR)) {
    	wrt << SHTC3_TRIG_H; // int type is handled as uint8_t
    	wrt << SHTC3_TRIG_L;
    }

    Above, the wrt object generated by the get_writer() method is used to write one byte at a time. The method parameter specifies the two-wire serial ID to write.

    1. The wrt object is created inside if(...). (The type is resolved as auto to avoid long type names.)
    2. The created wrt object defines operator bool () and is used for evaluation in the condition. If communication is possible with the specified ID, it returns true.
    3. The wrt object defines int operator () (void), which writes one byte to the two-wire serial bus when called. Returns -1 on failure, or the written byte value on success.
    4. The destructor of wrt is called at the end of the if() { ... } scope, issuing a STOP on the two-wire serial bus.

    get_writer()

    periph_wire::writer
    get_writer(uint8_t addr)

    Obtains a worker object used for I2C writing.

    ParameterDescription
    addrI2C address for writing

    Worker Object (writer)

    << operator

    operator << (int c)
    operator << (uint8_t c)
    operator << (uint16_t c)
    operator << (uint32_t c)

    int and uint8_t types transfer 8 bits. Data order is big-endian (higher byte transferred first).

    () operator

    operator() (uint8_t val)
    operator() (int val)

    Writes one byte.

    Worker Object (reader)

    >> operator

    operator >> (uint8_t& c)
    operator >> (uint16_t& c)
    operator >> (uint32_t& c)
    operator >> (uint8_t(&c)[N]) // fixed array of N bytes
    

    Reads the size of each data type. Data order is big-endian (first transferred byte is stored in the higher byte).

    () operator

    int operator() (bool b_stop = false)
    
    // example
    uint8_t dat[6];
    if (auto&& rdr = Wire.get_reader(0x70)) {
      for(uint8_t& x : dat) {
        x = rdr();
      }
    }

    Reads one byte. Returns -1 on error, or the read byte on success.

    If b_stop is set to true, a STOP bit is issued on that read.

    Example

    The following example is a measurement example for the environmental sensor pal’s temperature and humidity sensor SHTC3.

    Wire.begin();
    // reset (may not necessary...)
    if (auto&& wrt = Wire.get_writer(0x70)) {
    	wrt << 0x80; // SHTC3_SOFT_RST_H
    	wrt << 0x05; // SHTC3_SOFT_RST_L
    }
    
    delay(5); // wait some
    
    // start read
    if (auto&& wrt = Wire.get_writer(0x70)) {
    	wrt << 0x60; // SHTC3_TRIG_H
    	wrt << 0x9C; // SHTC3_TRIG_L
    }
    
    delay(10); // wait some
    
    // read result
    uint16_t u16temp, u16humd;
    uint8_t u8temp_csum, u8humd_csum;
    if (auto&& rdr = Wire.get_reader(0x70, 6)) {
    	rdr >> u16temp;
    	rdr >> u8temp_csum;
    	rdr >> u16humd;
    	rdr >> u8humd_csum;
    }
    
    // checksum 0x31, init=0xFF
    if (CRC8_u8CalcU16(u16temp, 0xff) != u8temp_csum) {
    	Serial << format("{SHTC3 T CKSUM %x}", u8temp_csum); }
    if (CRC8_u8CalcU16(u16humd, 0xff) != u8humd_csum) {
    	Serial << format("{SHTC3 H CKSUM %x}", u8humd_csum); }
    
    // calc temp/humid (degC x 100, % x 100)
    int16_t i16Temp = (int16_t)(-4500 + ((17500 * int32_t(u16temp)) >> 16));
    int16_t i16Humd = (int16_t)((int32_t(u16humd) * 10000) >> 16);
    
    Serial << "temp=" << int(i16Temp)
    	   << ",humid=" << int(i16Humd) << mwx::crlf;