Procedur von C script aufrufen

Mathrim

Benutzer
Beiträge
11
Hallo, ich bin noch recht frisch und der Datenbank Thematik und bitte daher um Nachsicht.

Ich habe mir eine Datenbank erstellt und dort eine Tabelle names "Measurement" angelegt
Code:
CREATE TABLE `Measurement` (
  `insertat` timestamp NULL DEFAULT current_timestamp(),
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `IDS` tinyint(3) unsigned DEFAULT NULL,
  `IDB` tinyint(3) unsigned DEFAULT NULL,
  `IDC` tinyint(3) unsigned DEFAULT NULL,
  `MoistMin` tinyint(3) unsigned DEFAULT NULL,
  `Moist` tinyint(3) unsigned DEFAULT NULL,
  `MoistMax` tinyint(3) unsigned DEFAULT NULL,
  `HumMin` tinyint(3) unsigned DEFAULT NULL,
  `Humidity` tinyint(3) unsigned DEFAULT NULL,
  `HumMax` tinyint(3) unsigned DEFAULT NULL,
  `Airpressure` int(10) unsigned DEFAULT NULL,
  `Temperature` float DEFAULT NULL,
  `Light` tinyint(3) unsigned DEFAULT NULL,
  `MoistAcition` int(11) GENERATED ALWAYS AS (if(`Moist` > `MoistMax`,2,if(`Moist` < `MoistMin`,0,1))) VIRTUAL,
  PRIMARY KEY (`id`),
  KEY `time_idx` (`insertat`),
  KEY `IDBC_idx` (`IDB`,`IDC`)
) ENGINE=InnoDB AUTO_INCREMENT=906 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

und eine Prozedur namens sensor_insert
Code:
DELIMITER ;;
CREATE DEFINER=`Supervisor`@`%` PROCEDURE `sensor_insert`(
    IN IDB INT,
    IN IDC INT,
    IN IDS INT,
    IN MoistMin INT,
    IN Moist INT,
    IN MoistMax INT,
    IN HumMin INT,
    IN Humidity DOUBLE,
    IN HumMax INT,
    IN Airpressure DOUBLE,
    IN Temperature DOUBLE,
    IN Light INT
)
BEGIN
    INSERT INTO GA_MK23.Measurement
    (IDB, IDC, IDS, MoistMin, Moist, MoistMax, HumMin, Humidity, HumMax, Airpressure, Temperature, Light)
    VALUES (IDB, IDC, IDS, MoistMin, Moist, MoistMax, HumMin, Humidity, HumMax, Airpressure, Temperature, Light);
END;;
DELIMITER ;

Wenn ich nun von meinem Sensor(ESP8266) die Prozedur aufrufe bekomme ich im General log und error log folgende nachricht

ERRORLOG

2025-01-03 15:27:14 38 [Warning] Aborted connection 38 to db: 'unconnected' user: 'Sensor' host: '192.168.4.3' (Got an error reading communication packets)
2025-01-03 15:27:27 48 [Warning] IP address '192.168.4.3' could not be resolved: Temporary failure in name resolution


GENERALLOG

Connect Sensor@192.168.4.3 on using TCP/IP
CALL sensor_insert(4,4,84,60,99,70,10,30.66,61,97.69,20.03,96)


Wenn ich dann in die Tabelle Measurement gucke ist dort keine neue Zeile.

Wenn ich die Procedur (mit dem Gleichen User wie im C Script) im Sequel ACE aufrufe funktioniert es wunderbar.
 
Werbung:
Also was ist das für ein C Script? Anhand der Überschrift hätte ich erwartet, es geht um den Script Code. Aber das ist nirgends zu sehen. Die SQL Prozedur funktioniert ja.
Wenn das C Script fehlerhaft ist, steigt das Programm aus und der Wert wird nicht eingefügt.
Außerdem frage ich mich, warum Du extra eine Prozedur statt eines einfachen Insert Befehls verwendest. Das Ganze ginge ohne die Prozedur. Man kann Prozeduren dafür nutzen, das ist nichts Böses, aber da Du sagst Du bist Einsteiger, frag ich mal nach dem Hintergrund.
 
Ich habe mich für eine Prozedur entschieden damit die Hauptarbeit von der Datenbank übernommen wird. Ich hatte mir erhofft so die Sendezeit zu reduzieren.



Code:
//alter String
char INSERT_SQL[] = "INSERT INTO GA_MK23.Measurement (IDB,IDC,IDS,MoistMin,Moist,MoistMax,HumMin,Humidity,HumMax,Airpressure,Temperature,Light) VALUES (%d,%d,%d,%d,%d,%d,%d,%.2f,%d,%.2f,%.2f,%d)";
//neuer String
char INSERT_SQL[] = "CALL sensor_insert(%d, %d, %d, %d, %d, %d, %d, %.2f, %d, %.2f, %.2f, %d)";


der Gesamte Code ist hier ( da wurde aber viel dran rumgebastelt, ist also etwas unübersichtlich

Code:
#include "ESP8266WiFi.h"
extern "C" {
#include <user_interface.h>
}


#define ets_wdt_disable ((void (*)(void))0x400030f0)
#define ets_delay_us ((void (*)(int))0x40002ecc)

#define _R (uint32_t*)0x60000700

void nk_deep_sleep(uint64_t time) {
  ets_wdt_disable();
  *(_R + 4) = 0;
  *(_R + 17) = 4;
  *(_R + 1) = *(_R + 7) + 5;
  *(_R + 6) = 8;
  *(_R + 2) = 1 << 20;
  ets_delay_us(10);
  *(_R + 39) = 0x11;
  *(_R + 40) = 3;
  *(_R) &= 0xFCF;
  *(_R + 1) = *(_R + 7) + (45 * (time >> 8));
  *(_R + 16) = 0x7F;
  *(_R + 2) = 1 << 20;
  __asm volatile("waiti 0");
}

#include <MySQL_Connection.h>
#include <MySQL_Cursor.h>
#include <Wire.h>
#include <SPI.h>
#include <BME280I2C.h>
#include <ESP_EEPROM.h>
#include <SoftwareSerial.h>

#define MUX_INPUT A0   //A0
#define WAKE 16        //D0
#define SCL 5          //D1
#define SDA 4          //D2
#define MUX_SELECT 12  //D3
#define RX 2           //D4
#define TX 0           //D6
#define PROG 14        //D5

//#define ErrorLED 13  //D7
#define PWR_PIN  13    //GPIO13



///////////////
//RECEVEDATA///
///////////////
const byte numChars = 5;
char receivedChars[numChars];
bool newData = false;
int numBytes = 0;
int ADCMoist = 0;
int ADCLight = 0;

bool ledState = LOW;
uint32_t previousMillis = 0;
int wait = 0;
SoftwareSerial mySerial(RX, TX);
//////////
//Prog//
//////////
int PoM = 0;
int value = 0;

////////////////
//SensorValues//
////////////////
//BME280//
BME280I2C bme;
BME280::TempUnit tempUnit(BME280::TempUnit_Celsius);
BME280::PresUnit presUnit(BME280::PresUnit_Pa);
float temperature, pressure, humidity;

//ANALOG VALUES//
byte moist, light, IDB, IDC,IDS, min_moist, max_moist, min_hum, max_hum, hum;
int32_t IDSe=0;
////////////////////////////
//WLAN und SQL SERVERDATEN//
////////////////////////////
IPAddress server_addr(192, 168, 4, 10);
char user[] = "Sensor";
char password[] = "ESP8266";

///////////////////
//NETWORKSETTINGS//
///////////////////
char ssid[] = "GA_MK23";
char pass[] = "1234567890";

/////////
//QUERY//
/////////
//char INSERT_SQL[] = "INSERT INTO GA_MK23.Measurement (IDB,IDC,IDS,MoistMin,Moist,MoistMax,HumMin,Humidity,HumMax,Airpressure,Temperature,Light) VALUES (%d,%d,%d,%d,%d,%d,%d,%.2f,%d,%.2f,%.2f,%d)";
char INSERT_SQL[] = "CALL sensor_insert(%d, %d, %d, %d, %d, %d, %d, %.2f, %d, %.2f, %.2f, %d)";
char query[128];
//char query[256];
WiFiClient client;
MySQL_Connection conn(&client);
MySQL_Cursor* cursor;
void setup() {

  //SetUp ESP PINS

  pinMode(TX, OUTPUT);
  pinMode(RX, INPUT_PULLUP);
  pinMode(PROG, INPUT);
  pinMode(MUX_SELECT, OUTPUT);
  pinMode(MUX_INPUT, INPUT);
  pinMode(PWR_PIN, OUTPUT);


  //StartSerial SorftwareSerial and I2C//
  Serial.begin(9600);
  mySerial.begin(9600);
  Wire.begin();
  EEPROM.begin(512);
  Serial.println("Start ESP");
  //get Data EEPROM
 
  IDB = EEPROM.read(0);
  IDC = EEPROM.read(1);
  min_moist = EEPROM.read(2);
  max_moist = EEPROM.read(3);
  min_hum = EEPROM.read(4);
  max_hum = EEPROM.read(5);
  IDS =EEPROM.read(6);
  Serial.print("IDB: ");
  Serial.println(EEPROM.read(0));
  Serial.print("IDC: ");
  Serial.println(IDC);
    Serial.print("IDS: ");
  Serial.println(IDS  );
  Serial.print("MAX HUM: ");
  Serial.println(max_hum);
  Serial.print("MIN HUM: ");
  Serial.println(min_hum);
  Serial.print("MAX MOIST: ");
  Serial.println(max_moist);
  Serial.print("MIN MOIST: ");
  Serial.println(min_moist);
  Serial.print("PROGPIN= ");
  Serial.println(PROG);


  -

    //check for Programmer is conected
    Serial.print("PROG ");
  Serial.println(digitalRead(PROG));
  if (!digitalRead(PROG)) {


    //Connect to WiFi
    Serial.printf("\nConnecting to %s", ssid);
    WiFi.begin(ssid, pass);
    while (WiFi.status() != WL_CONNECTED && wait < 20) {
      Blink( 1000);
    }  //While WIFI
    if (wait >= 20) {
      //digitalWrite(ErrorLED, LOW);
      ESP.restart();
    }
    wait = 0;
    // print out info about the connection:
    Serial.println("\nConnected to network");
    Serial.print("My IP address is: ");
    Serial.println(WiFi.localIP());

    //Test SQL ServerConnection
    Serial.print("Connecting to SQL...  ");

    if (conn.connect(server_addr, 3306, user, password))
      Serial.print("OK.");
    else {
      Serial.print("FAILED");
      while (wait <= 10) {
       Blink( 100);
      }  //while no SQL Connection
     // digitalWrite(ErrorLED, LOW);
      ESP.restart();
    }  //if Failed


    


    //initialize BME280 Sensor
    bme.begin();
    mySerial.flush();
  }

}  //Setup


void loop() {



 





  while (!digitalRead(PROG)) {
    //Get Values From EEPROM//
    Serial.print("IDB=");
    Serial.println(IDB);
    //Measure//
    //LIGHT//
    digitalWrite(MUX_SELECT, HIGH);
    ADCLight = analogRead(A0);
    light = (ADCLight * 100 / 1024);  //////////PRÜFEN//////////////

    Serial.print("LIGHT= ");
    Serial.println(analogRead(A0));
    //MOIST//
    digitalWrite(MUX_SELECT, LOW);
          ADCMoist = (analogRead(A0));
          clamp(660, 960);
    moist = map(ADCMoist, 660, 960, 99, 0);
    Serial.print("MOIST= ");
    Serial.println(analogRead(A0));
    Serial.print("MOIST%= ");
    Serial.println(moist);

    //PRES;TEMP;HUM//
    bme.read(pressure, temperature, humidity, tempUnit, presUnit);
    bme.read(pressure, temperature, humidity, tempUnit, presUnit);
    pressure = (pressure / 1000);
    Serial.print("pressure= ");
    Serial.println(pressure);
    Serial.print("temperature= ");
    Serial.println(temperature);
    Serial.print("humidity= ");
    Serial.println(humidity);
    IDSe=(IDB*1000+IDC*100+IDS);
    //Test Sensor is Connected to Database//
    

    
    
    
    
 if (conn.connected()) {
    // Cursor für die SQL-Anweisung erstellen
    MySQL_Cursor* cur_mem = new MySQL_Cursor(&conn);

    // Deine SQL-Insert-Abfrage erstellen
    sprintf(query, INSERT_SQL, IDB, IDC, IDS, min_moist, moist, max_moist, min_hum, humidity, max_hum, pressure, temperature, light);
    
    Serial.print("Send Data: ");
    Serial.println(query);

    // Starte eine Transaktion
    cur_mem->execute("START TRANSACTION");

    // SQL-Abfrage ausführen
    int res = cur_mem->execute(query);
    if (res) {
        Serial.println("Execute ok");

        // Transaktion bestätigen (commit)
        cur_mem->execute("COMMIT");
        Serial.println("Transaction committed");
    } else {
        // Fehlerbehandlung
        Serial.println("Execute Error");
        Serial.println(res);  // Fehlercode ausgeben
        
        // Falls ein Fehler auftritt, Rollback der Transaktion
        cur_mem->execute("COMMIT");
        Serial.println("Transaction committed");

        //cur_mem->execute("ROLLBACK");
       // Serial.println("Transaction rolled back");
    }

    // Den Cursor löschen, um die Ressourcen freizugeben
    delete cur_mem;
} else {
    Serial.println("MySQL connection not established.");
}


   Serial.println("PWR PIN HIGH");
digitalWrite(PWR_PIN, HIGH);
delay(10000);
  }  //while no programmer

  //WHILE PROGRAMMER
  recvWithEndMarker();
  ActAndReturn();
}  //LOOP
 
damit die Hauptarbeit von der Datenbank übernommen wird.
Ich denke, das ist ein häufiger Irrtum. Der Prozess läuft logisch so ab, dass (einmalig) Daten an den Server gesendet und eingefügt werden. Da kann man nichts optimieren. Eine Prozedur ist aus Performancesicht überflüssig oder sogar unnötiger Overhead. Sie hat/hätte allenfalls Sinn, wenn sie basierend auf einem Konzept / Interface / API den Zugriff kanalisiert, zusammen mit anderen Prozeduren und für einen einheitlichen Datenaustausch verschiedener "Parteien" sorgt.
Eine Prozedur kann technisch gesehen nicht magisch schneller sein als ein reines Insert sein, wenn sie nichts weiter tut als Daten anzunehmen und einzufügen, vollkommen analog zu dem Insert Befehl, der ohne Prozedur zu verwenden ist.
Eine Prozedur kann erst dann schneller sein, wenn sie Arbeit auf dem Server derart verrichtet, dass sie Daten (auf dem Server, lokal ohne Datenübertragung) auswertet und Ergebnisse direkt wieder in Tabellen schreibt (insert oder update ebenfalls lokal oder in lokale Reports/ Files). Damit spart man den Datentransport (plus Overhead) zwischen Client und Server.
Ich habe die C Routinen überflogen und nicht viel Ahnung davon. Grundsätzlich schon gar nicht zu den Eigenarten einer spezifischen C Implementierung des Datenbankzugriffs. Also das vorausgeschickt noch meine 5 Cent dazu, die für diese Implementierung nicht stimmen müssen.
Im Code sind mir 2 Stellen aufgefallen.
Das Erstellen einer Transaktion ist u.U. technisch überflüssig. Dazu ist es jedenfalls logisch überflüssig, da es lediglich um einen einzelnen Befehl geht, der per se aus Serversicht als eine Transaktion abgearbeitet wird. (Nebenbei: würde die Prozedur noch irgendetwas anderes transaktional relevantes -außer Logging bspw.- machen, gälte das immer noch und wäre ein anderer Grund, eine Prozedur zu verwenden, statt ein pures Insert, eben weil man eine einzige(!) Transaktion haben will, die der Server behandelt (nicht der Client).
Ein Cursor wäre m.E. notwendig, wenn ich auf einer Datenmenge arbeite, die im Server aufgebaut und durchlaufen wird, schrittweise, unidirektional oder bidirektional, je nach Bedarf. Auch das ist bei einem Insert nicht der Fall, also nicht nötig (oder sogar Teil des Problems), selbst wenn im (in einem einzelnen Befehl) Insert mehrere Datensätze erzeugt werden. Ein Insert, ein Update oder ein Delete produziert keine "greifbare" Datenmenge, nur ein Select macht das (oder eine StoredProcedure, die einen Cursor öffnet und als Datenmenge ausgibt, als hätte man eine Tabelle geöffnet).
 
Achso, was ich noch dazu sagen muss, ich hatte vorher das ganze über einen Insert geregelt, und das hat auch funktioniert
Ja, also einfache Antwort, bleib beim direkten Insert.

(außer wenn Du eine C-API aufbauen möchtest, was immer noch ohne Stored Procedures ginge. Erst wenn Du auf DB Seite eine API erstellen willst, bspw. um auch Berechtigungen schon auf DB Seite gezielt zu berücksichtigen/verwalten, wären StoredProcs auf dem Server hilfreich)
 
Mir ging es darum das der Sensor der das c Skript ausführt Batterie betrieben ist und die Daten via wifi sendet. Ich hatte mir eingebildet das es die Batterie schont wenn ich den String der zu senden ist verkürzen kann und so die Batterie etwas länger hält. Aktuell hält die Batterie 149 Tage bei einer Messung alle 15min also insgesamt ca. 14304 Messungen bei der Menge würde schon eine kleinste Einsparung pro Vorgang einen großen Effekt erzielen.
 
Werbung:
schon eine kleinste Einsparung pro Vorgang einen großen Effekt erzielen
Mmh, ob sich das bemerkbar macht? Du meinst, ein kürzerer SQL Befehl verbraucht weniger Batterie? Das kann man ja mglw mal an einem Tag ausprobieren und das Sende-Interval sehr klein setzen. Bei welcher Variante wird die Batterie länger halten ..?

Du kannst den Weg mit der Prozedur gehen, dann solltest Du Dich um meine Ideen zum Thema Transaktion und Cursor kümmern und den Code aufräumen (kostet ja auch alles).
Das Insert Statement kann aber vielleicht ebenfalls verkürzt werden, wenn Du die Auflistung der Felder weglässt. Das ist allerdings risikoreich, weil eine Änderung der Tabellenstruktur dann vom Insert nicht berücksichtigt werden kann. Das Insert muss dann angepasst werden.
 
Zurück
Oben