diff --git a/README.md b/README.md index 2d8776b0901b45210bc1b754c587c266625674e6..11ccccd1a2a5c8f55af2d9ddfe5234c2e1f95bc4 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,7 @@ Sensors - BH1750 light level sensor (`bh1750`) - DS18S20, DS1822, DS18B20, DS1825, DS28EA00, MAX31850K one-wire temperature sensors: (`ds18b`) - HC-SR04 ultrasonic distance sensor (`hcsr04`) +- MCP3008 analog digital converter (`mcp3008`) Streams ------- @@ -99,6 +100,74 @@ digital_inputs: pullup: yes pulldown: no ``` + +#### Temporary Set + +You may want to set the output to a given value for a certain amount of time. This can be done using the `/set_on_ms` and `/set_off_ms` topics. If an output is already set to that value, it will stay that value for the given amount of milliseconds and then switch to the opposite. + +For example, to set an output named `light` on for one second, publish `1000` as the payload to the `myprefix/output/light/set_on_ms` topic. + +If you want to force an output to always set to on/off for a configured amount of time, you can add `timed_set_ms` to your output config. This will mean that if you send "ON" to `myprefix/output/light/set`, then it will turn the light on for however many milliseconds are configured in `timed_set_ms` and then turn it off again. Whether the light is on already or not, sending "ON" will make the light eventually turn off after `timed_set_ms` milliseconds. This also works inversely with sending "OFF", which will turn the light off, then on after `timed_set_ms` milliseconds, so don't expect this to always keep your devices set to on/off. + +#### Interrupts + +Interrupts may be used for inputs instead of polling for raspberry modules. Specify `interrupt` and a strategy `rising`, `falling` or `both` to switch from polling to interrupt mode. The `bouncetime` is default `100ms` but may be changed (at least 1ms). The interrupt trigger will send a configurable `interrupt_payload` (default: `"INT"`) and not the current value of the pin: reading the current pin value in the ISR, returned 'old' values. Reading again in the ISR after 100ms gave 'changed' value, but waiting in ISR is not a good solution. So only a trigger message is transmitted on each ISR trigger. + +```yaml +digital_inputs: + - name: button_left + module: raspberrypi + pin: 23 + interrupt_payload: "trigger" + pullup: no + pulldown: yes + interrupt: falling + bouncetime: 200 +``` + +### Modules + +The IO modules are pluggable and multiple may be used at once. For example, if you have a Raspberry PI with some GPIO pins in use and also a PCF8574 IO expander on the I2C bus, you'd list two modules in the `gpio_modules` section and set up the inputs and outputs accordingly: + +```yaml +mqtt: + host: test.mosquitto.org + port: 1883 + user: "" + password: "" + topic_prefix: pimqttgpio/mydevice + +gpio_modules: + - name: raspberrypi + module: raspberrypi + + - name: pcf8574 + module: pcf8574 + i2c_bus_num: 1 + chip_addr: 0x20 + + - name: orangepi + module: orangepi + board: r1 + mode: board + +digital_inputs: + - name: button + module: raspberrypi + pin: 21 # This device is connected to pin 21 of the Raspberry PI GPIO + on_payload: "ON" + off_payload: "OFF" + pullup: no + pulldown: yes + +digital_outputs: + - name: bell + module: pcf8574 + pin: 2 # This device is connected to pin 2 of the PCF8574 IO expander + on_payload: "ON" + off_payload: "OFF" +``` + ### Sensors Receive updates on the value of a sensor by subscribing to the `home/sensor/<sensor input name>` topic. In the following example, this would be `home/sensor/temperature`: @@ -112,65 +181,73 @@ mqtt: topic_prefix: home sensor_modules: - - name: lm75 + - name: lm75_sensor module: lm75 i2c_bus_num: 1 chip_addr: 0x48 cleanup: no # This optional boolean value sets whether the module's `cleanup()` function will be called when the software exits. - - name: dht22 + - name: dht22_sensor module: dht22 type: AM2302 # can be DHT11, DHT22 or AM2302 pin: 4 - - name: bh1750 + - name: bh1750_sensor module: bh1750 i2c_bus_num: 1 chip_addr: 0x23 - - name: ds18b22 + - name: ds18b22_sensor module: ds18b type: DS18S20 address: 000803702e49 - - name: hcsr04 + - name: hcsr04_sensor module: hcsr04 pin_echo: 27 pin_trigger: 17 burst: 10 # number of measurements for output of distance value in [cm] + + - name: mcp3008_sensor + module: mcp3008 sensor_inputs: - - name: temperature - module: lm75 + - name: lm75_temperature + module: lm75_sensor interval: 15 #interval in seconds, that a value is read from the sensor and a update is published digits: 4 # number of digits to be round - name: dht22_temperature - module: dht22 + module: dht22_sensor interval: 10 #interval in seconds, that a value is read from the sensor and a update is published digits: 4 # number of digits to be round type: temperature # Can be temperature or humidity - name: dht22_humidity - module: dht22 + module: dht22_sensor interval: 10 #interval in seconds, that a value is read from the sensor and a update is published digits: 4 # number of digits to be round type: humidity # Can be temperature or humidity - name: bh1750_lux - module: bh1750 + module: bh1750_sensor interval: 10 digits: 2 -- name: ds18b22 - module: ds18b22 +- name: ds18b22_temperature + module: ds18b22_sensor interval: 60 digits: 2 - - name: distance - module: hcsr04 + - name: hcsr04_distance + module: hcsr04_sensor interval: 10 # measurement every 10s digits: 1 + + - name: mcp3008_voltage + module: mcp3008_sensor + interval: 300 # measurement every 5min + channel: CH4 # measure on CH4 of MCP3008 ``` ### Streams @@ -229,6 +306,7 @@ gpio_modules: board: zero # Supported: ZERO, R1, ZEROPLUS, ZEROPLUS2H5, ZEROPLUS2H3, PCPCPLUS, ONE, LITE, PLUS2E, PC2, PRIME mode: board ``` +### MQTT configuration #### SSL/TLS @@ -265,74 +343,7 @@ mqtt: insecure: yes ``` -#### Temporary Set - -You may want to set the output to a given value for a certain amount of time. This can be done using the `/set_on_ms` and `/set_off_ms` topics. If an output is already set to that value, it will stay that value for the given amount of milliseconds and then switch to the opposite. - -For example, to set an output named `light` on for one second, publish `1000` as the payload to the `myprefix/output/light/set_on_ms` topic. - -If you want to force an output to always set to on/off for a configured amount of time, you can add `timed_set_ms` to your output config. This will mean that if you send "ON" to `myprefix/output/light/set`, then it will turn the light on for however many milliseconds are configured in `timed_set_ms` and then turn it off again. Whether the light is on already or not, sending "ON" will make the light eventually turn off after `timed_set_ms` milliseconds. This also works inversely with sending "OFF", which will turn the light off, then on after `timed_set_ms` milliseconds, so don't expect this to always keep your devices set to on/off. - -#### Interrupts - -Interrupts may be used for inputs instead of polling for raspberry modules. Specify `interrupt` and a strategy `rising`, `falling` or `both` to switch from polling to interrupt mode. The `bouncetime` is default `100ms` but may be changed (at least 1ms). The interrupt trigger will send a configurable `interrupt_payload` (default: `"INT"`) and not the current value of the pin: reading the current pin value in the ISR, returned 'old' values. Reading again in the ISR after 100ms gave 'changed' value, but waiting in ISR is not a good solution. So only a trigger message is transmitted on each ISR trigger. - -```yaml -digital_inputs: - - name: button_left - module: raspberrypi - pin: 23 - interrupt_payload: "trigger" - pullup: no - pulldown: yes - interrupt: falling - bouncetime: 200 -``` - -### Modules - -The IO modules are pluggable and multiple may be used at once. For example, if you have a Raspberry PI with some GPIO pins in use and also a PCF8574 IO expander on the I2C bus, you'd list two modules in the `gpio_modules` section and set up the inputs and outputs accordingly: - -```yaml -mqtt: - host: test.mosquitto.org - port: 1883 - user: "" - password: "" - topic_prefix: pimqttgpio/mydevice - -gpio_modules: - - name: raspberrypi - module: raspberrypi - - - name: pcf8574 - module: pcf8574 - i2c_bus_num: 1 - chip_addr: 0x20 - - - name: orangepi - module: orangepi - board: r1 - mode: board - -digital_inputs: - - name: button - module: raspberrypi - pin: 21 # This device is connected to pin 21 of the Raspberry PI GPIO - on_payload: "ON" - off_payload: "OFF" - pullup: no - pulldown: yes - -digital_outputs: - - name: bell - module: pcf8574 - pin: 2 # This device is connected to pin 2 of the PCF8574 IO expander - on_payload: "ON" - off_payload: "OFF" -``` - -### MQTT Status Topic +#### MQTT Status Topic MQTT supports a "last will and testament" (LWT) feature which means the server will publish to a configured topic with a message of your choosing if it loses connection to the client unexpectedly. Using this feature, this project can be configured to publish to a status topic as depicted in the following example config: @@ -356,13 +367,13 @@ mqtt: the status messages will appear on topic `home/office/status`. -### MQTT Client ID +#### MQTT Client ID The MQTT client ID identifies your instance of pi-mqtt-gpio with your MQTT broker. It allows the broker to keep track of the state of your instance so that it can resume when it reconnects. This means that the ID _must_ be unique for each instance that connects to the MQTT broker. Since the MQTT client ID for each instance of pi-mqtt-gpio is based on the `topic_prefix` supplied in config (#24), having multiple instances share the same `topic_prefix` will require you to set a different `client_id` for each: -#### Device 1 +##### Device 1 ```yaml mqtt: @@ -371,7 +382,7 @@ mqtt: topic_prefix: home/office ``` -#### Device 2 +##### Device 2 ```yaml mqtt: diff --git a/pi_mqtt_gpio/modules/mcp3008.py b/pi_mqtt_gpio/modules/mcp3008.py new file mode 100644 index 0000000000000000000000000000000000000000..18c583a5464cf0437d45d56018208d85e7b72b85 --- /dev/null +++ b/pi_mqtt_gpio/modules/mcp3008.py @@ -0,0 +1,58 @@ +from pi_mqtt_gpio.modules import GenericSensor +import logging + +REQUIREMENTS = ("adafruit-mcp3008",) + +SENSOR_SCHEMA = { + "channel": dict( + type="string", + required=False, + empty=False, + default="CH0", + allowed=["CH0", "CH1", "CH2", "CH3", "CH4", "CH5", "CH6", "CH7", + "DF0", "DF1", "DF2", "DF3", "DF4", "DF5", "DF6", "DF7"], + ) +} + +_LOG = logging.getLogger("mqtt_gpio") + +class Sensor(GenericSensor): + """ + Implementation of MCP3008 ADC sensor. + """ + def __init__(self, config): + import Adafruit_MCP3008 + import Adafruit_GPIO.SPI as SPI + + """init the mcp on SPI CE0""" + SPI_PORT = 0 + SPI_DEVICE = 0 + self.mcp = Adafruit_MCP3008.MCP3008(spi=SPI.SpiDev(SPI_PORT, SPI_DEVICE)) + + self.channels = { + "CH0": 0, + "CH1": 1, + "CH2": 2, + "CH3": 3, + "CH4": 4, + "CH5": 5, + "CH6": 6, + "CH7": 7 + } + + def setup_sensor(self, config): + return True # nothing to do here + + def get_value(self, config): + """get the analog value from the adc for the configured channel""" + channel = self.channels.get(config["channel"], "invalid") + _LOG.warning("MCP3008: Reading from channel %r", channel) + if channel == "invalid": + raise Exception("Channel '" + config["channel"] + "' not found!") + value = self.mcp.read_adc(channel) + _LOG.warning("MCP3008: value %d", value) + return value + + def cleanup(self): + """close the adc, to proper shut down the spi bus""" + #self.adc.close()