yaacov / ArduinoModbusSlave

Modbus slave library for Arduino
ISC License
205 stars 75 forks source link

1-Wire / I2C / PWM #58

Open DpunktS opened 4 years ago

DpunktS commented 4 years ago

Is there a possibility to integrate 1-wire sensors (ds18b20), I2C sensors (BME280) and PWM in your code?

yaacov commented 4 years ago

yes, but you will need to be careful with timing.

the modbus pool routine and the connected devices communication routines need to be called in a way that does not break each other, can be tricky :-)

yaacov commented 4 years ago

p.s. If you get an example code to work ( e.g. modbus still answer requests and device comunication does not fail ) please add an example here for others that will have a similar problem.

p.p.s. added a "need help" label, in case someone already have a code example that do that :-)

ysmilda commented 4 years ago

Integrating different sensors and PWM into your modbus enabled sketch is very easy, as long as you make sure you're able to call the poll() function often enough.

How I usually do it is like this:

// Initialise modbus, sensors etc.

unsigned long last_measurement = 0;

void loop()
{
    watchdog.clear(); // Clear the watchdog.

    if ((millis() - last_measurement) > 6000) // Start measurement every 6 seconds.
    {
        last_measurement = millis();

        // Read your sensors here
    }
    else if (millis() < last_measurement)
    {
        last_measurement = millis();
    }

    slave.poll();
}

What it does is read out the sensors every 6 seconds and in in between those readings constantly poll the modbus serial line.

Also setting a PWM output using analogWrite() doesn't mess with the modbus timing.

DpunktS commented 4 years ago

Can you show me an example of a BME280 (i2c) sensor? I do not know exactly which I have to insert the sensor.

ysmilda commented 4 years ago

@DpunktS We're not here to create your projects for you. I will however give you some more guidelines as in how to read the sensor values via modbus.

What you want to do is choose a proper register type to read your data from. As it is a humidity sensor which is read only a Input Register seems to be the best fitting option. To this register/function code you will connect a function in the setup.

slave.cbVector[CB_READ_INPUT_REGISTERS] = your_function;

Now you have two options, either you read the sensor periodically in your main sketch (like described here) and store that value somewhere. This way you can manipulate the data and do stuff like averaging. your_function() would look something like this:

uint8_t your_function(uint8_t fc, uint16_t address, uint16_t length)
{
  slave.writeRegisterToBuffer(address, BME280_sensor_value);

  return STATUS_OK;
}

The other option is to read the sensor when you read the modbus register and then your_function() would look like this:

uint8_t your_function(uint8_t fc, uint16_t address, uint16_t length)
{
  slave.writeRegisterToBuffer(address, read_BME280_function());

  return STATUS_OK;
}

When you have multiple measurements/sensors the first version could be extended by storing the measured values in an array and read that out at various addresses. Hope this helps you finish your project.

DpunktS commented 4 years ago

I have now got it so far that the sketch runs but as soon as I query the sensor with address 10 the Arduino hangs. Can someone help with that again please.

// Handle the function code Read Input Registers (FC=04) and write back the values from
// the analog input pins (input registers).
uint8_t readAnalogIn(uint8_t fc, uint16_t address, uint16_t length)
{
  if (address == 10)
  {
    slave.writeRegisterToBuffer(10, bme280.getTemperature());

    return STATUS_OK;
  }

   else {// Check if the requested addresses exist in the array
        if (address > analog_pins_size || (address + length) > analog_pins_size)
        {
            return STATUS_ILLEGAL_DATA_ADDRESS;
        }

        // Read the analog inputs
        for (int i = 0; i < length; i++)
        {
            // Write the state of the analog pin to the response buffer.
            slave.writeRegisterToBuffer(i, analogRead(analog_pins[address + i]));
        }

        return STATUS_OK;
        }
}
Apulanta commented 4 years ago

Try if you are interested enough to edit "https://github.com/yaacov/arduino-irrigation-timer" application. It has an EEPROM as well as an I2C bus RTC device to which other I2C devices can be added. In this "irrigation-timer" the application writes directly to the modbus register and not to the buffer as that other. I made an EC-fan control app for this "irrigation-timer" with an OLED Adafruit_SSD1306 display which is updated in 1/1000 loop round and modbus works without any problems when making registry queries. I got a well-functioning and interesting application with eg: PID control, PWM output 0-10V, 0-10V Control voltage, NTC-10 temperature measurement, RPM measurement, OLED-SSD1306 display and modbus bus wirelessly with LORA radio.

ysmilda commented 4 years ago

@DpunktS usually when a i2c sensor hangs the Arduino when being read it is due to the Wire interface not initialised properly. Try calling Wire.begin() in your setup.

P.s. Also you shouldn't insert the measurement at place 10. This 10 refers to the amount of response values. Try writing the measurement to register 0

DpunktS commented 4 years ago

I have now managed to read a BME280. The sketch doesn't look nice, but it is enough for a beginner. Temperature, pressure, air humidity, absolute air humidity and dew point are transmitted and if the sensor is not connected, a 0 is displayed if the pullups keep SCL and SDA high. Maybe someone doesn't want to make the sketch beautiful and record it on Github. Modbus_BME280.txt

yaacov commented 4 years ago

@DpunktS nice !

ysmilda commented 4 years ago

@DpunktS As it seems that you're only using this to read the BME280 sensor I would simplify it to this: Modbus_BME280.txt That makes it easiere for you to follow your logic and expand it further.

By adding sensor names to the enum you automatically enlarge the struct which can be read over modbus. This way you can add any measurement to modbus easily.

DpunktS commented 4 years ago

hello i still use the analog and digital pins + bme280. The Arduino controls and monitors a heating circuit distributor heating / cooling.