I2CWorker - vlizBE/dcafs GitHub Wiki

This manual won't go into the details of explaining what I2C is and how it's used. For that you should check the datasheet of the device you wish to implement.

Setup

See i2c:? for the possible commands, the one to start with is i2c:adddevice,id,bus,address,script
Suggest we want to add the BME280 that uses address 0x76 on controller 0, this would become:
i2c:adddevice,bme,0,0x76,BME280 and in settings.xml the following section will be added (minus the comments).

<i2c>
      <bus controller="0">
        <device address="0x76" id="bme" script="bme280"/>
    <!--
      id -> this is used to identify the device in telnet and TaskManager etc
      script -> id for the command script and name of the corresponding file in i2cscripts
      address -> the 7-bit address of the device on the bus in hexadecimal format
      Optionally, label can be set
      -->
	</bus>
</i2c>

And a blank commandset file in i2cscripts to get started:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<commandset script="bme280">
  <!-- An empty command to start with -->
  <command id="cmdname" info="what this does"/>
</commandset>

Commandset

Currently there are five different instructions:

  • read: to read data from a device
  • write: to write data to a device
  • alter: to read a register, apply a logical operation and write the result to the register read
  • math: to apply a mathematical operation on read data

Read

An example of a command with only reads

<commandset script="bme280">
   	<command id="calc_hum" info="Read the humidity registers."  msbfirst="false">
		<read reg="0xFD" msbfirst="true" bits="16" return="2"/>
		<read reg="0xA1" bits="8"  signed="false"  return="1"/> <!-- H1: unsigned 8bit -->
		<read reg="0xE1" bits="16" signed="true"   return="2"/> <!-- H2: signed 16bit  -->
		<read reg="0xE3" bits="8"  signed="false"  return="1"/> <!-- H3: unsigned 8bit -->
		<read reg="0xE4" msbfirst="true" bits="12" signed="true"  return="2"/> <!-- H4: signed 12bit  -->
		<read reg="0xE5" bits="12" signed="true"   return="2"/> <!-- H5: humidity signed short -->
		<read reg="0xE5" bits="8"  signed="true"   return="1"/>  <!-- H6: humidity signed char -->
	</command>
</commandset>

Attributes

Most attributes in a single read node can also be an attribute in the command node, when present in the command node this is considered the default for all read nodes unless the read node overrides this. Furthermore if not present in either node, the standard defaults are taken (see below).

Options:

  • msbfirst For multibyte reads, this determines if the most significant byte is received first (true or false), default is true
  • signed Whether or not the result is signed 2's complement, default is false
  • return The total amount of bytes to read
  • bits how many bits should be combined to a single int, options: 8,10,12,16,20,24

Note: Not sure yet if the current way of bits/return is intuitive...
There are two options:

  • return is the amount of bytes that are read and the amount of bits determines how these are divided in integers
  • return is the amount of integers you want and with the amount of bits, the amount of bytes is calculated

Forgot why I picked the first option...

So for example

<read reg="0xFD" msbfirst="true" bits="16" return="2"/>
<!--
 reg="0xFD" -> the register to start the read at is 0xFD
 msbfirst="true -> the integers are send msbfirst, this is the default but the command node overrides this default to false
 bits="16" -> Each integer is 16 bits long
 return="2" -> Two bytes so 16 bit should be read, or a single 16bit integer

 signed isn't mentioned, nor in the command node so this is false (globab default)
-->
<read reg="0xE5" bits="12" signed="true"   return="2"/>
<!--
 reg="0xE5" -> the register to start the read at is 0xFD
 bits="12" -> Each integer is 12 bits long
 signed="true"
 return="2" -> Two bytes for a total of 16bits will be read, but only use 12 of them

 msbfirst is false (command node default) but still is still todo for 10 and 12 bits,
 for now it takes the full first byte and only the MS nibble/two bits of the second
-->

On the I2C bus the first example read would look like this:
[START]I2C_Address[W][A]->0xFD[AS]->[Start]I2C_Address[R][S]->readByte[0][AM]->readByte[1][NAK][STOP]

In which:

  • [start] is the start condition from the master
  • [W] last bit of the address is set to signify a write operation
  • [R] last bit of the address is cleared to signify a read operation
  • [stop] is the stop condition from the master
  • [AS] is an acknowledge from the slave
  • [AM] is an acknowledge from the master
  • [NAK] is a 'Not' acknowlegde issued by the master to tell the slave to stop sending

Write

An example of a command with only writes:

<command id="weather" info="Set weather recommends (1 sample/min, no filter, no oversampling)">
	<write reg="0xF5" >0x00</write> <!-- standby dont care (forced mode), filter off, no spi 3w -->
	<write reg="0xF2" >0x01</write> <!-- Humidity oversampling -->
	<write reg="0xF4" >0x26 0x52</write> 
</command>

Which is a lot simpler than the read operation, in I2C protocol, this looks like this
[START]I2C_Address[W][A]->0xF5[AS]->0x00[STOP]
[START]I2C_Address[W][A]->0xF2[AS]->0x01[STOP]
[START]I2C_Address[W][A]->0xF4[AS]->0x26[AS]->0x52[STOP]

Alter

This instruction is used to change the content of a byte register based on the current value and a logical operand.

<alter reg="0x0F" operand="or">0x10</alter> <!-- Read register 0x0F and apply 'OR 0x10' and overwrite it -->
<alter reg="0x0F" operand="and">0x10</alter> <!-- Read register 0x0F and apply 'AND 0x10' and overwrite it -->
<alter reg="0x0F" operand="xor">0x10</alter> <!-- Read register 0x0F and apply 'XOR 0x10' and overwrite it -->
<alter reg="0x0F" operand="not"></alter> <!-- Read register 0x0F, invert the result and overwrite it -->

Wait_ack

This instruction is to make execution of the folllowing instructions wait till the device has responded to a address probe with Acknowlegde.

<!-- This will do up to 15 probe attempts until either an [AS] was received or 15 fails and the command is cancelled -->
<wait_ack>15</wait_ack> 

This results in the following I2C operation: [START]I2C_Address [START]I2C_Address [START]I2C_Address ... [START]I2C_Address[AS]

Math

This instruction is to perform mathematical operations on received data before it's send to targets. It follows the same rules as all the other math functionality

<command id="senses" info="Read the sense of the both outputs" bits="10"> 		
	<read reg="0xE0" return="2"/> 
        <!-- Because the packs per 10bits and we read 2 bytes, from this point i0 exists -->
	<read reg="0xE2" return="2"/> 
        <!-- From this point i1 exists -->
	<math>i0=i0*62.5</math>	<!-- i0 refers to the first value read -->	
	<math>i1=i1*62.5</math> <!-- i1 refers to the second value read -->
        <math>i1=i0*256+i1</math> <!-- This is allowed to -->
</command> 

Using commandsets

To use a command created with the script, type the following i2c:device id,command or to use the earlier xml as an example i2c:bme,calc_hum

Note: device-id is case insensitive but command id isn't

What the above actually does:

  • Check if there's a device with the id bme
  • If so check if the script bme280 contains a command calc_hum
  • If so, add the command to the execution queue
  • If data was read:
    • If the command had a label, the data would have been passed to the BaseWorker which processes it according to the label
    • If any writables are registered for the device, they get deviceid;cmdid;integer0;...;integerx as a string

Tasks

Given that all system commands can be used in TaskManager with the system output, the earlier i2c command can be used in a Task like this:

<!-- This will run the command 5 seconds after DAS has finished start-up -->
<task output="system" trigger="delay:5s" >i2c:bme,calc_hum</task>

Note: I2C commands are added to a queue so will be executed in order and one at a time. For now one worker services all controllers this might change in the future to have one worker per controller.

⚠️ **GitHub.com Fallback** ⚠️