char string hochzählen?

Hallo,

ich beschäftige mich immer wieder mit meiner Sensorerfassung (Nanoclon mit wz5100 clon)für meineHeimautomatisierung.
Es werden momentan nds18b20, 1bmp180 und 2*dht22 Sensoren erfasst und mittels mqtt Protokoll in mein heimnetz geschickt und dort weiterverarbeitet (fhem auf einem raspberry).
Ebenso kann die Sensorerfassung auf mqtt Nachrichten im Netz reagieren.

Hier nun erstmal der ganze Code:

#include <SPI.h>
#include <Ethernet.h>
#include <PubSubClient.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <dht.h>
#include <SFE_BMP180.h>
#include <Wire.h>

//------------LED---------------------

const int ledPin =  3;

int ledState = LOW;             // ledState used to set the LED

//------------1Wire---------------------

// Data wire is plugged into port 2 on the Arduino
#define ONE_WIRE_BUS 7
// #define TEMPERATURE_PRECISION 9

// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);

// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);

//-------------DHT-----------------------

dht DHT;

byte DHT22_PIN;

//------------- BMP180 --------------

SFE_BMP180 pressure;

#define ALTITUDE 221.0 // Altitude in meter

//----------------Netzwerk---------------

byte mac[]    = {  0xDE, 0xED, 0xBA, 0x1A, 0x1A, 0x1A };
IPAddress ip(192, 168, 0, 210);
IPAddress server(192, 168, 0, 201);

EthernetClient ethClient;

PubSubClient client(ethClient);


long lastReconnectAttempt = 0;
unsigned long previousMillis = 0;
unsigned long currentMillis = 0;
unsigned long interval = 10000;

//------------------------------------------------------------------
void setup()
{

  pinMode(ledPin, OUTPUT);

  Serial.begin(115200);

  // Startup BMP180
  pressure.begin();

  // Start up the Dallas library
  sensors.begin();

  // locate devices on the bus
  Serial.print(F("Locating devices on Bus..."));
  Serial.print(F("Found "));
  Serial.print(sensors.getDeviceCount(), DEC);
  Serial.println(F(" devices."));

  // report parasite power requirements
  Serial.print("Parasite power is: ");
  if (sensors.isParasitePowerMode()) Serial.println("ON");
  else Serial.println("OFF");
  //-----------------------------

  client.setServer(server, 1883);
  client.setCallback(callback);

  Ethernet.begin(mac, ip);
  // Allow the hardware to sort itself out
  delay(1500);
}

void loop()
{
  if (!client.connected()) {
    reconnect();
  }
  client.loop();

  currentMillis = millis();

  if (currentMillis - previousMillis >= interval) {

    DHT_shout();

    ds18b20_reading();

    bmp180();



    previousMillis = currentMillis;
  }
}

void DHT_reading()
{
  // READ DATA
  Serial.println();
  int chk = DHT.read22(DHT22_PIN);


}

void DHT_shout()
{
  DHT22_PIN = 5;
  DHT_reading();

  // DISPLAY DATA
  Serial.println(F("DHT Sensors"));

  Serial.print(F("Sensor 0: h:"));
  Serial.print(DHT.humidity, 1);
  Serial.print(F(" t:"));
  Serial.print(DHT.temperature, 1);

  publish(PSTR("dht_hum_0"), DHT.humidity);
  publish(PSTR("dht_temp_0"), DHT.temperature);

  delay (5);

  DHT22_PIN = 6;
  DHT_reading();

  // DISPLAY DATA
  Serial.print(F("Sensor 1: h:"));
  Serial.print(DHT.humidity, 1);
  Serial.print(F(" t:"));
  Serial.println(DHT.temperature, 1);

  publish(PSTR("dht_hum_1"), DHT.humidity);
  publish(PSTR("dht_temp_1"), DHT.temperature);

  Serial.println();
  DHT22_PIN = 5;
}

void ds18b20_reading()
{
  // call sensors.requestTemperatures() to issue a global temperature
  // request to all devices on the bus
  Serial.print(F("Request DS18B20 temps..."));
  sensors.requestTemperatures(); // Send the command to get temperatures
  Serial.println(F("DONE"));

  ds18b20_shout();
}

void ds18b20_shout()
{
  for (uint8_t i = 0; i < sensors.getDeviceCount(); i++)
  {
  Serial.print(F("Temp device (index "));
  Serial.print(i);
  Serial.print(F(") is: "));
  Serial.println(sensors.getTempCByIndex(i));
  publish(PSTR("temp_index_"), sensors.getTempCByIndex(i));
  
  
  }
}

void bmp180()
{
  char status;
  double T, P, p0;

  // Loop here getting pressure readings every 10 seconds.

  // If you want sea-level-compensated pressure, as used in weather reports,
  // you will need to know the altitude at which your measurements are taken.
  // We're using a constant called ALTITUDE in this sketch:

  Serial.println();
  Serial.print(F("altitude: "));
  Serial.print(ALTITUDE, 0);
  Serial.println(F(" meters, "));

  // If you want to measure altitude, and not pressure, you will instead need
  // to provide a known baseline pressure. This is shown at the end of the sketch.

  // You must first get a temperature measurement to perform a pressure reading.

  // Start a temperature measurement:
  // If request is successful, the number of ms to wait is returned.
  // If request is unsuccessful, 0 is returned.

  status = pressure.startTemperature();
  if (status != 0)
  {
    Serial.println(F("Request bmp180 val... "));
    // Wait for the measurement to complete:
    delay(status);

    // Retrieve the completed temperature measurement:
    // Note that the measurement is stored in the variable T.
    // Function returns 1 if successful, 0 if failure.

    status = pressure.getTemperature(T);
    if (status != 0)
    {
      // Print out the measurement:
      Serial.print(F("temperature: "));
      Serial.print(T, 2);
      Serial.println(F(" deg C, "));

      publish(PSTR("bmp180_temperature"), T);

      // Start a pressure measurement:
      // The parameter is the oversampling setting, from 0 to 3 (highest res, longest wait).
      // If request is successful, the number of ms to wait is returned.
      // If request is unsuccessful, 0 is returned.

      status = pressure.startPressure(3);
      if (status != 0)
      {
        // Wait for the measurement to complete:
        delay(status);

        // Retrieve the completed pressure measurement:
        // Note that the measurement is stored in the variable P.
        // Note also that the function requires the previous temperature measurement (T).
        // (If temperature is stable, you can do one temperature measurement for a number of pressure measurements.)
        // Function returns 1 if successful, 0 if failure.

        status = pressure.getPressure(P, T);
        if (status != 0)
        {
          p0 = pressure.sealevel(P, ALTITUDE); // we're at 221 meters (Boulder, CO)
          Serial.print(F("relative (sea-level) pressure: "));
          Serial.print(p0, 2);
          Serial.println(F(" mb, "));

          publish(PSTR("bmp180_pressure"), p0);
          Serial.println(F("...Done"));

        }
      }
    }
  }
}

// ===========================================================
// Callback Funktion von MQTT. Die Funktion wird aufgerufen
// wenn ein Wert empfangen wurde.
// ===========================================================
void callback(char* topic, byte* payload, unsigned int length) {
  // Zähler
  int i = 0;
  // Hilfsvariablen für die Convertierung der Nachricht in ein String
  char message_buff[100];

  Serial.println("Message arrived: topic: " + String(topic));
  Serial.println("Length: " + String(length, DEC));

  // Kopieren der Nachricht und erstellen eines Bytes mit abschließender \0
  for (i = 0; i < length; i++) {
    message_buff[i] = payload[i];
  }
  message_buff[i] = '\0';

  // Konvertierung der nachricht in ein String
  String msgString = String(message_buff);
  Serial.println("Payload: " + msgString);

  if (String(topic) == "in1Topic") {
    if (msgString == "on")ledState = HIGH;
    if (msgString == "off")ledState = LOW;
  }
  Serial.println(ledState);
  digitalWrite(ledPin, ledState);
}


void reconnect()
{
  // Loop until we're reconnected
  while (!client.connected()) {

    long now = millis();
    if (now - lastReconnectAttempt > 5000) {
      lastReconnectAttempt = now;
      Serial.print(F("failed, rc="));
      Serial.print(client.state());
      Serial.println(F(" try again in 5 seconds"));
      // Wait 5 seconds before retrying
      // Attempt to connect
      if (client.connect("arduinoClient")) {
        Serial.println(F("connected"));
        lastReconnectAttempt = 0;
        // ... and resubscribe
        client.subscribe("inTopic");
        client.subscribe("in1Topic");
      }
    }
  }
}

void publish(const char* str, float val)
{
  char buffer[15];
  char message_buff[30];
  strncpy_P(message_buff, str, sizeof(message_buff));
  dtostrf(val, 1, 1, buffer);
  client.publish(message_buff, buffer);
}

weiter im nächter post…

Ich habe ihn dahingehend umgebaut, dass ich dynamisch auf die Anzahl der ds18b20 Sensoren reagieren will, das probiere ich momentan so:

void ds18b20_shout()
{
  for (uint8_t i = 0; i < sensors.getDeviceCount(); i++)
  {
  Serial.print(F("Temp device (index "));
  Serial.print(i);
  Serial.print(F(") is: "));
  Serial.println(sensors.getTempCByIndex(i));
  publish(PSTR("temp_index_"), sensors.getTempCByIndex(i));

postet mir je nach Anzahl der angeschlossenen Sensoren die Werte ins Netz.
Problem ist, dass ich nicht weiß wie ich den string “temp_index_” (in der letzten Zeile) hochzählen kann, damit er unterscheidbar wird, etwa so “temp_index_1”, “temp_index_2” usw.
Ich hoffe ich konnte mich verständlich ausdrücken, habe leider nur rudimentäre Kenntnisse und klicke mich halt so durch Beispiele und lese viel hier im Forum.

Häng doch einfach den Zähler i an temp_index_ dran

void setup() {
  char buffer[20];
  Serial.begin(115200);
  for (byte num = 1; num < 10; num++) {
    sprintf_P(buffer, PSTR("temp_index_%d"), num);
    Serial.println(buffer);
    sprintf_P(buffer, PSTR("temp_index_%02d"), num);
    Serial.println(buffer);
    sprintf_P(buffer, PSTR("%S%d"), PSTR("temp_index_"), num);
    Serial.println(buffer);
  }
}

void loop() { }
temp_index_1
temp_index_01
temp_index_1
temp_index_2
temp_index_02
temp_index_2
temp_index_3
temp_index_03
temp_index_3
temp_index_4
temp_index_04
temp_index_4
temp_index_5
temp_index_05
temp_index_5

Also ich habe jetzt noch ein wenig rumprobiert.

Die Funktion

void publish(const char* str, float val)
{
  char buffer[15];
  char message_buff[30];
  strncpy_P(message_buff, str, sizeof(message_buff));
  dtostrf(val, 1, 1, buffer);
  client.publish(message_buff, buffer);
}

erwartet ja als Übergabewert "const char* str" ich verstehe es trotz des Beispiels von Whandall leider nicht. Evtl kann mir jemad das noch näher erklären

Ich habe es mal so probiert

void ds18b20_shout()
{
  for (uint8_t i = 0; i < sensors.getDeviceCount(); i++)
  {
    char  buffer [20];
    Serial.print(F("Temp device (index "));
    Serial.print(i);
    Serial.print(F(") is: "));
    Serial.println(sensors.getTempCByIndex(i));
    sprintf(buffer, ("temp_index_%d"), i);
    publish (buffer, sensors.getTempCByIndex(i));
    Serial.println(buffer);

  }

buffer hat zwar den richtigen “wert”, aber er scheint nicht richtig auf die funktion

void publish(const char* str, float val)
{
  char buffer[15];
  char message_buff[30];
  strncpy_P(message_buff, str, sizeof(message_buff));
  dtostrf(val, 1, 1, buffer);
  client.publish(message_buff, buffer);
}

übergeben zu werden. Was mache ich falsch?
Ist es schlimm wenn ich “buffer” in beiden funktionen verwenden? Nein, weil nicht global, oder?

Da der Text jetzt zusammengebastelt wird, kann er nicht mehr im PROGMEM liegen, publish erwartet aber einen solchen.

void publishRam(const char* str, float val)
{
  char buffer[15];
  dtostrf(val, 1, 1, buffer);
  client.publish(str, buffer);
}

Ist dir klar was das _P genau bedeutet? Dazu müsste str im Flash liegen. Das ist hier nicht der Fall. Der Compiler kann das aber nicht unterscheiden. Deshalb compiliert es, aber produziert dann zur Laufzeit Unsinn.

Serenifly: Ist dir klar was das _P genau bedeutet? Dazu müsste str im Flash liegen. Das ist hier nicht der Fall. Der Compiler kann das aber nicht unterscheiden. Deshalb compiliert es, aber produziert dann zur Laufzeit Unsinn.

zu meiner Schande, ich habe das vergessen :confused: , du selbst hast es mir in einem anderen Thread gesagt, ich zitiere: PSTR() = Progmem String. Definiert einen C String im Flash. Das kannst du aber nur hier verwenden! Wenn du das bei einer Funktion verwendest du einen String im RAM erwartet (was bei den aller meisten der Fall ist), kompiliert das zwar, aber läuft nicht richtig!

Whandall: Da der Text jetzt zusammengebastelt wird, kann er nicht mehr im PROGMEM liegen, publish erwartet aber einen solchen.

void publishRam(const char* str, float val)
{
  char buffer[15];
  dtostrf(val, 1, 1, buffer);
  client.publish(str, buffer);
}

danke für die passende Lösung, ich habe die Funktion entsprechend eingebaut. So ist es sowieso besser, weil mir kaum noch Programmspeicher bleibt.

Ich wollte mich hier nochmal zu Wort melden! :)

Ich bin gerade noch dabei einen "low-cost-Wifi-mqtt-client" mit einem ESP8266 zu entwerfen (incl. deep sleep), den ich mit der Arduino IDE programmiere. Alles läuft bisher ganz gut. Auf folgendes bin ich in der Dokumentation gestoßen:

Progmem

The Program memory features work much the same way as on a regular Arduino; placing read only data and strings in read only memory and freeing heap for your application. The important difference is that on the ESP8266 the literal strings are not pooled. This means that the same literal string defined inside a F("") and/or PSTR("") will take up space for each instance in the code. So you will need to manage the duplicate strings yourself.

There is one additional helper macro to make it easier to pass const PROGMEM strings to methods that take a __FlashStringHelper called FPSTR(). The use of this will help make it easier to pool strings. Not pooling strings...

String response1;
response1 += F("http:");
...
String response2;
response2 += F("http:");

using FPSTR would become...

const char HTTP[] PROGMEM = "http:";
...
{
    String response1;
    response1 += FPSTR(HTTP);
    ...
    String response2;
    response2 += FPSTR(HTTP);
}

Da ich bei der ganzen Sache noch nicht sorecht durchsteige :o , kann mir evtl. einer erklären inwiefern sich das auf meine obigen Funktionen

void publish(const char* str, float val)
{
  char buffer[20];
  char message_buff[30];
  strncpy_P(message_buff, str, sizeof(message_buff));
  dtostrf(val, 1, 1, buffer);
  client.publish(message_buff, buffer);
}

void publishRam(const char* str, float val)
{
  char buffer[20];
  dtostrf(val, 1, 1, buffer);
  client.publish(str, buffer);
}

im Zusammenhang mit einem ESP8266 auswirkt. Werte übergeben ich z.b ja so: publish(PSTR("dht_temp_1"), DHT.temperature);

Das beschreibt ja nur ein paar Unterschiede, Pooling/NonPooling und das neue Macro.

Ich glaube das hat keinen Einfluss auf deinen Kode, vielleicht wird er etwas größer.