aboutsummaryrefslogtreecommitdiff
path: root/software/michi_funcs
diff options
context:
space:
mode:
authorMDC Service <michael.schmid@mdc-service.de>2022-05-23 11:17:34 +0200
committerGitHub <noreply@github.com>2022-05-23 11:17:34 +0200
commit74a0003fea4087de8e9f03b6f828c45d255bcd4a (patch)
tree1b4615232dcac918fcc47cbd3c6ed54014fb38b7 /software/michi_funcs
parentd2f3c7c9e17895ae43caed761f941a30e4e5eed6 (diff)
initial check in firmware files
Diffstat (limited to 'software/michi_funcs')
-rw-r--r--software/michi_funcs/AW9523B.cpp225
-rw-r--r--software/michi_funcs/AW9523B.hpp151
-rw-r--r--software/michi_funcs/SimplePgSQL.cpp1278
-rw-r--r--software/michi_funcs/SimplePgSQL.h278
-rw-r--r--software/michi_funcs/SimplePgSQL_cpp.old900
-rw-r--r--software/michi_funcs/SimplePgSQL_h.old209
-rw-r--r--software/michi_funcs/board_rev_A.cpp38
-rw-r--r--software/michi_funcs/board_rev_A.hpp49
-rw-r--r--software/michi_funcs/board_rev_B.cpp102
-rw-r--r--software/michi_funcs/board_rev_B.hpp63
-rw-r--r--software/michi_funcs/component.mk4
-rw-r--r--software/michi_funcs/db_funcs.cpp404
-rw-r--r--software/michi_funcs/db_funcs.h66
-rw-r--r--software/michi_funcs/sdcard_funcs.cpp153
-rw-r--r--software/michi_funcs/sdcard_funcs.hpp6
15 files changed, 3926 insertions, 0 deletions
diff --git a/software/michi_funcs/AW9523B.cpp b/software/michi_funcs/AW9523B.cpp
new file mode 100644
index 0000000..bc65ace
--- /dev/null
+++ b/software/michi_funcs/AW9523B.cpp
@@ -0,0 +1,225 @@
+/**************************************************************************/
+/*!
+ @file AW9523B.cpp
+
+ Author: Michi
+ License: MIT (see LICENSE)
+*/
+/**************************************************************************/
+
+
+#ifdef STM32
+ #include "stm32l4xx_hal.h"
+#else
+ #include <sys/types.h>
+ #include <stdio.h>
+ #include "driver/i2c.h"
+ #define I2C_MASTER_TIMEOUT_MS 1000
+#endif
+#include "AW9523B.hpp"
+
+
+//#include <stdint.h>
+//#include <stdbool.h>
+
+
+#define _REG(port, reg) (port == P0 ? reg : reg + 1)
+
+AW9523B *i2c_gpio;
+
+uint8_t AW9523B::readI2C(uint8_t reg)
+{
+ //AW9523B_REG_ID
+ uint8_t gelesen = 0;
+ uint16_t MemAddress;
+ MemAddress = reg;
+//HAL_StatusTypeDef HAL_I2C_Mem_Read(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress, uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout)
+#ifdef STM32
+ HAL_StatusTypeDef ret;
+ ret = HAL_I2C_Mem_Read( &(this->i2c_handle), _addr, MemAddress, 1, &gelesen, 1, HAL_MAX_DELAY);
+ if (ret != HAL_OK)
+#else
+ esp_err_t res1;
+ printf("I2C read I2C-Bus:%d Addr:%d Register: %d BEGIN \n", this->i2c_num, _addr, reg );
+ //res1 = i2c_master_read_from_device(this->i2c_num, _addr, /*i2c_buf*/&gelesen, 1, I2C_MASTER_TIMEOUT_MS / portTICK_RATE_MS);
+ res1 = i2c_master_write_read_device(this->i2c_num, _addr, &reg, 1, &gelesen, 2, I2C_MASTER_TIMEOUT_MS / portTICK_RATE_MS);
+ printf(" --> gelesen Byte: %d \n", gelesen );
+ if (res1 != ESP_OK)
+#endif
+ { printf("ERROR I2C read I2C -------------------------- \n"); ESP_ERROR_CHECK_WITHOUT_ABORT(res1); return 0; }
+ else { printf(" I2C read I2C-Bus OK \n"); return gelesen; }
+}
+
+//bool i2c_write(uint16_t MemAddress, uint8_t *pData)
+uint8_t AW9523B::writeI2C(uint8_t reg, uint8_t val)
+{
+ //AW9523B_REG_ID
+ uint16_t MemAddress;
+ MemAddress = reg;
+ //HAL_I2C_Mem_Write(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint16_t MemAddress,
+ //uint16_t MemAddSize, uint8_t *pData, uint16_t Size, uint32_t Timeout)
+#ifdef STM32
+ HAL_StatusTypeDef ret;
+ ret = HAL_I2C_Mem_Write( &(this->i2c_handle), _addr, MemAddress, 1, &val, 1, HAL_MAX_DELAY);
+ if (ret != HAL_OK)
+#else
+ uint8_t I2C_buf[3];
+ esp_err_t res1 = ESP_OK;
+ I2C_buf[0] = reg; //0x02; // write to register 0x02
+ I2C_buf[1] = 255;
+ I2C_buf[2] = 255;
+ //res1 = i2c_master_write_to_device(I2C_MASTER_NUM, I2C_PORTEXP1, I2C_buf, 3, I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS);
+ //ESP_ERROR_CHECK(res1 );
+ if (res1 != ESP_OK)
+#endif
+ { printf("ERROR I2C write I2C -------------------------- \n"); ESP_ERROR_CHECK_WITHOUT_ABORT(res1); return false; }
+ else { printf(" I2C write I2C OK \n"); return true; }
+}
+
+
+#ifdef STM32
+AW9523B::AW9523B(uint8_t address, I2C_HandleTypeDef _i2c_handle) //AD[1:0] address offset
+{
+ i2c_handle = _i2c_handle;
+ _addr = AW9523B_I2C_ADDRESS + (address & 0x03);
+#else
+AW9523B::AW9523B(uint8_t address, int _i2c_num) //AD[1:0] address offset
+{
+ i2c_num = _i2c_num; //I2C number, 0 or 1 on ESP32
+ _addr = 0x58;/*AW9523B_I2C_ADDRESS; 0x58 for first AW9523, 0x58 for the second */
+#endif
+}
+
+bool AW9523B::begin()
+{
+// Wire.begin();
+// return readI2C(AW9523B_REG_ID) == AW9523B_ID;
+#ifdef STM32
+ HAL_StatusTypeDef ret;
+ ret = HAL_I2C_IsDeviceReady (&i2c_handle, _addr, 1, 1000);
+ if (ret == HAL_OK)
+#else
+ uint8_t I2C_buf[3];
+ esp_err_t res1 = ESP_OK, res2 = ESP_OK;
+ I2C_buf[0] = 0x7F; // write to soft-RESET register on AW9523
+ I2C_buf[1] = 0x00; // write 0 to reset the AW9523
+// res1 = i2c_master_write_to_device(I2C_MASTER_NUM, I2C_PORTEXP1, I2C_buf, 2, I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS);
+// res2 = i2c_master_write_to_device(I2C_MASTER_NUM, I2C_PORTEXP2, I2C_buf, 2, I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS);
+ printf("\nMaster wrote %d %d %02X\n", res1,res2,I2C_buf[0]);
+
+ if ( readI2C(AW9523B_REG_ID) == AW9523B_ID )
+#endif
+ {
+ return true;
+ } else { return false; }
+}
+
+void AW9523B::scanAllAddress(void)
+{
+ esp_err_t res1;
+ uint8_t gelesen = 0;
+ uint8_t reg_addr = AW9523B_REG_ID;
+ printf("Scanning I2C bus for devices:\n");
+ fflush(stdout);
+ for (int i=0; i<255; i++)
+ {
+ //i2c_master_write_read_device(I2C_MASTER_NUM, slave_addr, &reg_addr, 1, data, 2, I2C_MASTER_TIMEOUT_MS / portTICK_RATE_MS);
+ //res1 = i2c_master_read_from_device(0, i, AW9523B_REG_ID, 1, &gelesen, 1, I2C_MASTER_TIMEOUT_MS / portTICK_RATE_MS);
+ res1 = i2c_master_write_read_device(0, i, &reg_addr, 1, &gelesen, 1, I2C_MASTER_TIMEOUT_MS / portTICK_RATE_MS);
+ if (res1 == ESP_OK)
+ {
+ printf("\ndevice found on I2C-address %d, read value: %d AW9523=16 \n", i, gelesen);
+
+ }
+ else { printf("."); }
+ fflush(stdout);
+ gelesen = 0;
+ }
+ printf("\nfinish I2C Bus scan\n");
+}
+
+void AW9523B::configInOut(AW9523BPort port, uint8_t inout)
+{
+ writeI2C(_REG(port, AW9523B_P0_CONF_STATE), inout);
+}
+
+void AW9523B::configLedGPIO(AW9523BPort port, uint8_t ledGpio)
+{
+ writeI2C(_REG(port, AW9523B_P0_LED_MODE), ledGpio);
+}
+
+void AW9523B::setPort0Mode(AW9523BPortMode mode)
+{
+ writeI2C(AW9523B_REG_GLOB_CTR, mode | ledsDim);
+}
+
+void AW9523B::setLedsDim(AW9523BLedsDim dim)
+{
+ writeI2C(AW9523B_REG_GLOB_CTR, dim | portMode);
+}
+
+void AW9523B::setLed(AW9523BLedDimCtrl led, uint8_t value)
+{
+ writeI2C(led, value);
+}
+
+void AW9523B::portIn(AW9523BPort port)
+{
+ _portIn = _REG(port, AW9523B_P0_IN_STATE);
+}
+
+void AW9523B::portOut(AW9523BPort port)
+{
+ _portOut = _REG(port, AW9523B_P0_OUT_STATE);
+}
+
+uint8_t AW9523B::read()
+{
+ return readI2C(_portIn);
+}
+
+uint8_t AW9523B::read(AW9523BPort port)
+{
+ return readI2C(AW9523B_P0_IN_STATE + port);
+}
+
+uint8_t AW9523B::write(uint8_t data)
+{
+ return writeI2C(_portOut, data);
+}
+
+uint8_t AW9523B::write(AW9523BPort port, uint8_t data)
+{
+ return writeI2C(AW9523B_P0_OUT_STATE + port, data);
+}
+
+void AW9523B::reset()
+{
+ writeI2C(AW9523B_REG_SOFT_RST, 0);
+}
+
+#ifdef STM32
+ extern "C" bool AW9523B_init(uint8_t address, I2C_HandleTypeDef _i2c_handle)
+ {
+ i2c_gpio = new AW9523B(address, _i2c_handle);
+ if (!i2c_gpio->begin() )
+ {
+ //Serial.println("Error: AW9523B not found!");
+ return false;
+ } else
+ {
+ i2c_gpio->reset();
+ i2c_gpio->setPort0Mode(PUSH_PULL);
+ return true;
+ }
+ }
+ extern "C" void AW9523B_destroy(void)
+ {
+ delete i2c_gpio;
+ }
+ extern "C" void AW9523B_setAllOutputHigh(void)
+ {
+ i2c_gpio->write(P0, 255);
+ i2c_gpio->write(P1, 255);
+ }
+#endif
diff --git a/software/michi_funcs/AW9523B.hpp b/software/michi_funcs/AW9523B.hpp
new file mode 100644
index 0000000..4699a02
--- /dev/null
+++ b/software/michi_funcs/AW9523B.hpp
@@ -0,0 +1,151 @@
+
+/**************************************************************************/
+/*!
+ @file AW9523B.h
+
+ Author: Manuel Polo (https://about.me/mrmx)
+ License: MIT (see LICENSE)
+*/
+/**************************************************************************/
+
+#ifndef _AW9523B_H_
+#define _AW9523B_H_
+
+//#include <Arduino.h>
+//#include <stdint.h>
+//#include <stdbool.h>
+
+#ifdef STM32
+ #include "stm32l4xx_hal.h"
+#endif
+
+
+/** Registers */
+#define AW9523B_I2C_ADDRESS 0x58 ///< I2C base address for AW9523B
+#define AW9523B_REG_ID 0x10 ///< id register
+#define AW9523B_ID 0x23 ///< id value
+#define AW9523B_P0_IN_STATE 0x00 ///< P0 port input state
+#define AW9523B_P1_IN_STATE 0x01 ///< P1 port input state
+#define AW9523B_P0_OUT_STATE 0x02 ///< P0 port output state
+#define AW9523B_P1_OUT_STATE 0x03 ///< P1 port output state
+#define AW9523B_P0_CONF_STATE 0x04 ///< P0 port config state
+#define AW9523B_P1_CONF_STATE 0x05 ///< P1 port config state
+#define AW9523B_REG_GLOB_CTR 0x11 ///< Global control register
+#define AW9523B_P0_LED_MODE 0x12 ///< P0 port led mode switch register
+#define AW9523B_P1_LED_MODE 0x13 ///< P1 port led mode switch register
+#define AW9523B_REG_SOFT_RST 0x7F ///< Soft reset register
+
+
+
+/** AW9523B Port constants */
+enum AW9523BPort
+{
+ P0 = 0x00, // Port 0
+ P1 = 0x01, // Port 1
+};
+
+enum AW9523BPortMode
+{
+ OPEN_DRAIN = 0x00, // Port 0 open drain mode
+ PUSH_PULL = 1 << 4 // Port 0 push pull mode
+};
+
+/** AW9523B Port0 LED dimmer constants: 256 step dimming range select*/
+enum AW9523BLedsDim
+{
+ DIM_MAX = 0x00,//B00, // 0~IMAX 37mA(typical)
+ DIM_MED = 0x01,//B01, // 0~(IMAX×3/4)
+ DIM_LOW = 0x02,//B10, // 0~(IMAX×2/4)
+ DIM_MIN = 0x03,//B11, // 0~(IMAX×1/4)
+};
+
+/** AW9523B LED dimm current control registers*/
+enum AW9523BLedDimCtrl
+{
+ P1_0 = 0x20, // DIM0
+ P1_1 = 0x21, // DIM1
+ P1_2 = 0x22, // DIM2
+ P1_3 = 0x23, // DIM3
+ P0_0 = 0x24, // DIM4
+ P0_1 = 0x25, // DIM5
+ P0_2 = 0x26, // DIM6
+ P0_3 = 0x27, // DIM7
+ P0_4 = 0x28, // DIM8
+ P0_5 = 0x29, // DIM9
+ P0_6 = 0x2A, // DIM10
+ P0_7 = 0x2B, // DIM11
+ P1_4 = 0x2C, // DIM12
+ P1_5 = 0x2D, // DIM13
+ P1_6 = 0x2E, // DIM14
+ P1_7 = 0x2F, // DIM15
+};
+
+// Uncomment to enable debug messages
+//#define AW9523B_DEBUG
+
+// Define where debug output will be printed
+#define DEBUG_PRINTER Serial
+
+// Setup debug printing macros
+#ifdef AW9523B_DEBUG
+#define DEBUG_PRINT(...) \
+ { \
+ DEBUG_PRINTER.print(__VA_ARGS__); \
+ }
+#define DEBUG_PRINTLN(...) \
+ { \
+ DEBUG_PRINTER.println(__VA_ARGS__); \
+ }
+#else
+#define DEBUG_PRINT(...) \
+ { \
+ }
+#define DEBUG_PRINTLN(...) \
+ { \
+ }
+#endif
+
+/**************************************************************************/
+/*!
+ @brief AW9523B I2C 16bit GPIO expander and LED driver
+*/
+/**************************************************************************/
+class AW9523B
+{
+
+public:
+#ifdef STM32
+ AW9523B(uint8_t address, I2C_HandleTypeDef _i2c_handle); //AD[1:0] address offset
+#else
+ AW9523B(uint8_t address, int _i2c_num); //AD[1:0] address offset
+#endif
+ bool begin();
+ void configInOut(AW9523BPort port, uint8_t inout);
+ void configLedGPIO(AW9523BPort port, uint8_t ledGpio);
+ void setPort0Mode(AW9523BPortMode mode);
+ void setLedsDim(AW9523BLedsDim value);
+ void setLed(AW9523BLedDimCtrl led, uint8_t value);
+ void portIn(AW9523BPort port);
+ void portOut(AW9523BPort port);
+ uint8_t read();
+ uint8_t read(AW9523BPort port);
+ uint8_t write(uint8_t data);
+ uint8_t write(AW9523BPort port, uint8_t data);
+ void reset();
+ void scanAllAddress(void);
+private:
+ uint8_t writeI2C(uint8_t reg, uint8_t val);
+ uint8_t readI2C(uint8_t reg);
+ uint8_t _addr;
+ uint8_t _portIn = AW9523B_P0_IN_STATE;
+ uint8_t _portOut = AW9523B_P0_OUT_STATE;
+ AW9523BPortMode portMode = OPEN_DRAIN;
+ AW9523BLedsDim ledsDim = DIM_MAX;
+#ifdef STM32
+ I2C_HandleTypeDef i2c_handle;//Michi
+#else
+ /*i2c_port_t*/int i2c_num;
+#endif
+};
+
+#endif // _AW9523B_H_
diff --git a/software/michi_funcs/SimplePgSQL.cpp b/software/michi_funcs/SimplePgSQL.cpp
new file mode 100644
index 0000000..0d1612a
--- /dev/null
+++ b/software/michi_funcs/SimplePgSQL.cpp
@@ -0,0 +1,1278 @@
+/*
+ * SimplePgSQL.c - Lightweight PostgreSQL connector for Arduino
+ * Copyright (C) Bohdan R. Rau 2016 <ethanak@polip.com>
+ *
+ * SimplePgSQL is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * SimplePgSQL is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with SimplePgSQL. If not, write to:
+ * The Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301, USA.
+ */
+
+ // 238 469 / 34004
+
+ //version 2022-02-17
+
+#include "SimplePgSQL.h"
+
+
+
+#ifdef ESP32
+#define strchr_P strchr
+#endif
+
+#ifdef PG_USE_MD5
+static void
+bytesToHex(const uint8_t b[16], char *s)
+{
+ int q, w;
+#ifndef ESP32
+ static PROGMEM const char hex[] = "0123456789abcdef";
+ for (q = 0, w = 0; q < 16; q++)
+ {
+ s[w++] = pgm_read_byte(&hex[(b[q] >> 4) & 0x0F]);
+ s[w++] = pgm_read_byte(&hex[b[q] & 0x0F]);
+ }
+#else
+ static const char hex[] = "0123456789abcdef";
+ for (q = 0, w = 0; q < 16; q++)
+ {
+ s[w++] = hex[(b[q] >> 4) & 0x0F];
+ s[w++] = hex[b[q] & 0x0F];
+ }
+#endif
+ s[w] = '\0';
+}
+
+#ifdef ESP32_IDF
+#include "esp_log.h"
+#endif
+
+#ifdef ESP32
+#include <mbedtls/md5.h>
+
+static void pg_md5_encrypt(const char *password, char *salt, int salt_len, char *outbuf)
+{
+ md5_context_t context;
+ uint8_t sum[16];
+ *outbuf++ = 'm';
+ *outbuf++ = 'd';
+ *outbuf++ = '5';
+ mbedtls_md5_init(&context);
+ mbedtls_md5_update_ret(&context, (uint8_t *)password, strlen(password));
+ mbedtls_md5_update_ret(&context, (uint8_t *)salt, salt_len);
+ mbedtls_md5_finish_ret( &context, sum);
+ bytesToHex(sum, outbuf);
+}
+#elif defined(ESP8266)
+#include <md5.h>
+static void pg_md5_encrypt(const char *password, char *salt, int salt_len, char *outbuf)
+{
+ md5_context_t context;
+ uint8_t sum[16];
+ *outbuf++ = 'm';
+ *outbuf++ = 'd';
+ *outbuf++ = '5';
+ MD5Init(&context);
+ MD5Update(&context, (uint8_t *)password, strlen(password));
+ MD5Update(&context, (uint8_t *)salt, salt_len);
+ MD5Final(sum, &context);
+ bytesToHex(sum, outbuf);
+}
+#else
+//#include <MD5.h>
+static void pg_md5_encrypt(const char *password, char *salt, int salt_len, char *outbuf)
+{
+ MD5_CTX context;
+ uint8_t sum[16];
+ *outbuf++ = 'm';
+ *outbuf++ = 'd';
+ *outbuf++ = '5';
+
+ MD5::MD5Init(&context);
+ MD5::MD5Update(&context, (uint8_t *)password, strlen(password));
+ MD5::MD5Update(&context, (uint8_t *)salt, salt_len);
+ MD5::MD5Final(sum, &context);
+ bytesToHex(sum, outbuf);
+}
+#endif
+#endif
+
+#define MD5_PASSWD_LEN 35
+#define AUTH_REQ_OK 0 /* User is authenticated */
+#define AUTH_REQ_PASSWORD 3 /* Password */
+#define AUTH_REQ_MD5 5 /* md5 password */
+
+#ifndef ESP32_IDF
+static PROGMEM const char EM_OOM [] = "Out of memory";
+static PROGMEM const char EM_READ [] = "Backend read error";
+static PROGMEM const char EM_WRITE [] = "Backend write error";
+static PROGMEM const char EM_CONN [] = "Cannot connect to server";
+static PROGMEM const char EM_SYNC [] = "Backend out of sync";
+static PROGMEM const char EM_INTR [] = "Internal error";
+static PROGMEM const char EM_UAUTH [] = "Unsupported auth method";
+static PROGMEM const char EM_BIN [] = "Binary format not supported";
+static PROGMEM const char EM_EXEC [] = "Previous execution not finished";
+static PROGMEM const char EM_PASSWD [] = "Password required";
+static PROGMEM const char EM_EMPTY [] = "Query is empty";
+static PROGMEM const char EM_FORMAT [] = "Illegal formatting character";
+#else
+static const char EM_OOM [] = "Out of memory";
+static const char EM_READ [] = "Backend read error";
+static const char EM_WRITE [] = "Backend write error";
+static const char EM_CONN [] = "Cannot connect to server";
+static const char EM_SYNC [] = "Backend out of sync";
+static const char EM_INTR [] = "Internal error";
+static const char EM_UAUTH [] = "Unsupported auth method";
+static const char EM_BIN [] = "Binary format not supported";
+static const char EM_EXEC [] = "Previous execution not finished";
+static const char EM_PASSWD [] = "Password required";
+static const char EM_EMPTY [] = "Query is empty";
+static const char EM_FORMAT [] = "Illegal formatting character";
+
+#define PGTAG "PGSQL"
+#endif
+
+#ifndef ESP32_IDF
+PGconnection::PGconnection(Client *c,
+ int flags,
+ int memory,
+ char *foreignBuffer)
+{
+ conn_status = CONNECTION_NEEDED;
+ client = c;
+ Buffer = foreignBuffer;
+ _passwd = NULL;
+ _flags = flags & ~PG_FLAG_STATIC_BUFFER;
+
+ if (memory <= 0) bufSize = PG_BUFFER_SIZE;
+ else bufSize = memory;
+ if (foreignBuffer) {
+ _flags |= PG_FLAG_STATIC_BUFFER;
+ }
+}
+#else
+PGconnection::PGconnection(const int flags, const unsigned char *_Buffer, const int _bufSize) {
+ conn_status = CONNECTION_NEEDED;
+ _passwd = NULL;
+ _user = NULL;
+ Buffer = (char *) _Buffer;
+ bufSize = _bufSize;
+ bufPos = 0;
+ result_status=0;
+ _available=0;
+ _nfields=0;
+ _ntuples=0;
+ _flags=0;
+
+ conn_status = CONNECTION_NEEDED;
+ _flags = flags & ~PG_FLAG_STATIC_BUFFER;
+ bufSize = PG_BUFFER_SIZE;
+}
+#endif
+
+#ifndef ESP32_IDF
+int PGconnection::setDbLogin(IPAddress server,
+ const char *user,
+ const char *passwd,
+ const char *db,
+ const char *charset,
+ int port)
+{
+
+ char *startpacket;
+ int packetlen;
+ int len;
+
+ close();
+ if (!db) db = user;
+ len = strlen(user) + 1;
+ if (passwd) {
+ len += strlen(passwd) + 1;
+ }
+ _user = (char *)malloc(len);
+ strcpy(_user, user);
+ if (passwd) {
+ _passwd = _user + strlen(user) + 1;
+ strcpy(_passwd, passwd);
+ }
+ else {
+ _passwd = NULL;
+ }
+ if (!Buffer) Buffer = (char *) malloc(bufSize);
+ byte connected = client -> connect(server, port);
+ if (!connected) {
+ setMsg_P(EM_CONN, PG_RSTAT_HAVE_ERROR);
+ return conn_status = CONNECTION_BAD;
+ }
+ packetlen = build_startup_packet(NULL, db, charset);
+ if (packetlen > bufSize - 10) {
+ setMsg_P(EM_OOM, PG_RSTAT_HAVE_ERROR);
+ conn_status = CONNECTION_BAD;
+ return conn_status;
+ }
+ startpacket=Buffer + (bufSize - (packetlen + 1));
+ build_startup_packet(startpacket, db, charset);
+ if (pqPacketSend(0, startpacket, packetlen) < 0) {
+ setMsg_P(EM_WRITE, PG_RSTAT_HAVE_ERROR);
+ return conn_status = CONNECTION_BAD;
+ }
+ attempts = 0;
+ return conn_status = CONNECTION_AWAITING_RESPONSE;
+}
+#else
+int PGconnection::setDbLogin(const char *ServerIP, int ServerPort, const char *dbName, const char *dbUser, const char *dbPasswd, const char *dbCharset) {
+
+ char *startpacket;
+ int packetlen;
+ int len;
+
+// close();
+ memset(&DestAddr, 0, sizeof(DestAddr));
+ AddrFamily = AF_INET;
+ ipProtocol = IPPROTO_IP;
+ DestAddr.sin_addr.s_addr = inet_addr(ServerIP);
+ DestAddr.sin_family = AF_INET;
+ DestAddr.sin_port = htons(ServerPort);
+
+ if (!dbName)
+ dbName = dbUser;
+ len = strlen(dbUser) + 1;
+ if (dbPasswd) {
+ len += strlen(dbPasswd) + 1;
+ }
+ _user = (char *) malloc(len);
+ strcpy(_user, dbUser);
+ if (dbPasswd) {
+ _passwd = _user + strlen(dbUser) + 1;
+ strcpy(_passwd, dbPasswd);
+ } else {
+ _passwd = NULL;
+ }
+
+ //int8_t connected = connect(client->connect(server, port);
+ SockH = socket(AddrFamily, SOCK_STREAM, ipProtocol);
+ if (SockH < 0) {
+ ESP_LOGE(PGTAG, "Unable to create socket: errno %d", errno);
+ setMsg_P(EM_CONN, PG_RSTAT_HAVE_ERROR);
+ return conn_status = CONNECTION_BAD;
+ } else {
+ int err = connect(SockH, (struct sockaddr *) &DestAddr, sizeof(DestAddr));
+ if (err != 0) {
+ ESP_LOGE(PGTAG, "Socket unable to connect: errno %d", errno);
+ return conn_status = CONNECTION_BAD;
+ }
+ NetConnected = 1;
+ ESP_LOGI(PGTAG, "Successfully connected");
+ }
+
+ packetlen = build_startup_packet(NULL, dbName, dbCharset);
+ if (packetlen > bufSize - 10) {
+ setMsg_P(EM_OOM, PG_RSTAT_HAVE_ERROR);
+ conn_status = CONNECTION_BAD;
+ return conn_status;
+ }
+
+ startpacket = Buffer + (bufSize - (packetlen + 1));
+ build_startup_packet(startpacket, dbName, dbCharset);
+ if (pqPacketSend(0, startpacket, packetlen) < 0) {
+ setMsg_P(EM_WRITE, PG_RSTAT_HAVE_ERROR);
+ return conn_status = CONNECTION_BAD;
+ }
+ attempts = 0;
+ return conn_status = CONNECTION_AWAITING_RESPONSE;
+}
+
+void close_socket(int _SockH)
+{
+ close(_SockH);
+}
+#endif
+
+void PGconnection::close(void)
+{
+#ifndef ESP32_IDF
+ if (client->connected()) {
+#else
+ if (NetConnected) {
+#endif
+ pqPacketSend('X', NULL, 0);
+#ifndef ESP32_IDF
+ client->stop();
+#else
+ shutdown(SockH, 0);
+ close_socket(SockH);
+#endif
+ }
+ if (Buffer && !(_flags & PG_FLAG_STATIC_BUFFER)) {
+ free(Buffer);
+ Buffer = NULL;
+ }
+ if (_user) {
+ free(_user);
+ _user = _passwd = NULL;
+ }
+ conn_status = CONNECTION_NEEDED;
+
+#ifndef ESP32_IDF
+ NetConnected = 0;
+#endif
+}
+
+#ifdef ESP32_IDF
+int PGconnection::dataAvailable() {
+ int res=0;
+// if (_available) return _available;
+ ioctl(SockH,FIONREAD,&res);
+ return res;
+}
+#endif
+
+int PGconnection::status(void)
+{
+ char bereq;
+ char rc;
+ int32_t msgLen;
+ int32_t areq;
+ char * pwd = _passwd;
+#ifdef PG_USE_MD5
+ char salt[4];
+#endif
+
+ switch(conn_status) {
+ case CONNECTION_NEEDED:
+ case CONNECTION_OK:
+ case CONNECTION_BAD:
+
+ return conn_status;
+
+ case CONNECTION_AWAITING_RESPONSE:
+#ifndef ESP32_IDF
+ if (!client->available()) return conn_status;
+#else
+ if (dataAvailable() == 0) return conn_status;
+#endif
+ if (attempts++ >= 2) {
+ setMsg_P(EM_SYNC, PG_RSTAT_HAVE_ERROR);
+ return conn_status = CONNECTION_BAD;
+ }
+ if (pqGetc(&bereq)) {
+ goto read_error;
+ }
+ if (bereq == 'E') {
+ pqGetInt4(&msgLen);
+ pqGetNotice(PG_RSTAT_HAVE_ERROR);
+ return conn_status = CONNECTION_BAD;
+ }
+ if (bereq != 'R') {
+ setMsg_P(EM_SYNC, PG_RSTAT_HAVE_ERROR);
+ return conn_status = CONNECTION_BAD;
+ }
+ if (pqGetInt4(&msgLen)) {
+ goto read_error;
+ }
+ if (pqGetInt4(&areq)) {
+ goto read_error;
+ }
+ if (areq == AUTH_REQ_OK) {
+ if (_user) {
+ free(_user);
+ _user = _passwd=NULL;
+ }
+ result_status = PG_RSTAT_READY;
+ return conn_status = CONNECTION_AUTH_OK;
+ }
+ if (
+#ifdef PG_USE_MD5
+ areq != AUTH_REQ_MD5 &&
+#endif
+ areq != AUTH_REQ_PASSWORD) {
+ setMsg_P(EM_UAUTH, PG_RSTAT_HAVE_ERROR);
+ return conn_status = CONNECTION_BAD;
+ }
+ if (!_passwd || !*_passwd) {
+ setMsg_P(EM_PASSWD, PG_RSTAT_HAVE_ERROR);
+ return conn_status = CONNECTION_BAD;
+ }
+ pwd = _passwd;
+#ifdef PG_USE_MD5
+ if (areq == AUTH_REQ_MD5) {
+ if (pqGetnchar(salt, 4)) goto read_error;
+ if (bufSize < 3 * MD5_PASSWD_LEN + 10) {
+ setMsg_P(EM_OOM, PG_RSTAT_HAVE_ERROR);
+ return conn_status = CONNECTION_BAD;
+ }
+ char *crypt_pwd = Buffer + (bufSize - (2 * (MD5_PASSWD_LEN + 1)));
+ char *crypt_pwd2 = crypt_pwd + MD5_PASSWD_LEN + 1;
+ pg_md5_encrypt(pwd, _user, strlen(_user), crypt_pwd2);
+ pg_md5_encrypt(crypt_pwd2 + 3, salt,4, crypt_pwd);
+ pwd = crypt_pwd;
+ }
+#endif
+ rc=pqPacketSend('p', pwd, strlen(pwd) + 1);
+ if (rc) {
+ goto write_error;
+ }
+ return conn_status;
+
+ case CONNECTION_AUTH_OK:
+ for (;;) {
+#ifndef ESP32_IDF
+ if (!client -> available()) return conn_status;
+#else
+ if (dataAvailable() == 0) return conn_status;
+#endif
+ if (pqGetc(&bereq)) goto read_error;
+ if (pqGetInt4(&msgLen)) goto read_error;
+ msgLen -= 4;
+ if (bereq == 'A' || bereq == 'N' || bereq == 'S' || bereq == 'K') {
+ if (pqSkipnchar(msgLen)) goto read_error;
+ continue;
+ }
+ if (bereq == 'E') {
+ pqGetNotice(PG_RSTAT_HAVE_ERROR);
+ return conn_status = CONNECTION_BAD;
+ }
+
+/* if (bereq == 'K') {
+ if (pqGetInt4(&be_pid)) goto read_error;
+ if (pqGetInt4(&be_key)) goto read_error;
+ continue;
+ }
+*/
+ if (bereq == 'Z') {
+ pqSkipnchar(msgLen);
+ return conn_status = CONNECTION_OK;
+ }
+ return conn_status = CONNECTION_BAD;
+ }
+
+ default:
+ setMsg_P(EM_INTR, PG_RSTAT_HAVE_ERROR);
+ return conn_status = CONNECTION_BAD;
+ }
+read_error:
+ setMsg_P(EM_READ, PG_RSTAT_HAVE_ERROR);
+ return conn_status = CONNECTION_BAD;
+write_error:
+ setMsg_P(EM_WRITE, PG_RSTAT_HAVE_ERROR);
+ return conn_status = CONNECTION_BAD;
+}
+
+int PGconnection::execute(const char *query, int progmem)
+{
+#ifndef ESP32
+// for unknown reason, this status check get wrong on ESP32-IDF.
+// uncommenting solve the problem for now, but need invesitgation
+ if (!(result_status & PG_RSTAT_READY)) {
+ setMsg_P(EM_EXEC, PG_RSTAT_HAVE_ERROR);
+ return -1;
+ }
+#endif
+ int len =
+#ifndef ESP32
+ progmem ? strlen_P(query) :
+#endif
+ strlen(query);
+
+#ifndef ESP32_IDF
+ if (pqPacketSend('Q', query, len+1, progmem)) {
+#else
+ if (pqPacketSend('Q', query, len + 1)) {
+#endif
+ setMsg_P(EM_WRITE, PG_RSTAT_HAVE_ERROR);
+ conn_status = CONNECTION_BAD;
+ return -1;
+ }
+ result_status = PG_RSTAT_COMMAND_SENT;
+ return 0;
+
+}
+/*
+int PGconnection::PGexecute(const char *query) {
+ int len = strlen(query);
+ if (pqPacketSend('Q', query, len + 1)) {
+ setMsg_P(EM_WRITE, PG_RSTAT_HAVE_ERROR);
+ conn_status = CONNECTION_BAD;
+ return -1;
+ }
+ result_status = PG_RSTAT_COMMAND_SENT;
+ return 0;
+}
+*/
+
+
+int PGconnection::escapeName(const char *inbuf, char *outbuf)
+{
+ const char *c;
+ int l = 2;
+ for (c=inbuf; *c; c++) {
+ l++;
+ if (*c == '\\' || *c == '"') l++;
+ }
+ if (outbuf) {
+ *outbuf++='"';
+ for (c=inbuf; *c; c++) {
+ *outbuf++ = *c;
+ if (*c == '\\' || *c == '"') *outbuf++ = *c;
+ }
+ *outbuf++='"';
+ }
+ return l;
+}
+
+int PGconnection::escapeString(const char *inbuf, char *outbuf)
+{
+ const char *c;
+ int e = 0, l;
+ for (c=inbuf; *c; c++) {
+ if (*c == '\\' || *c == '\'') e++;
+ }
+ l = e + (c - inbuf) + (e ? 4 : 2);
+ if (outbuf) {
+ if (e) {
+ *outbuf++=' ';
+ *outbuf++='E';
+ }
+ *outbuf++='\'';
+ for (c=inbuf; *c; c++) {
+ *outbuf++ = *c;
+ if (*c == '\\' || *c == '\'') *outbuf++ = *c;
+ }
+ *outbuf++='\'';
+ }
+ return l;
+}
+
+char * PGconnection::getValue(int nr)
+{
+ int i;
+ if (_null & (1<<nr)) return NULL;
+ char *c=Buffer;
+ if (nr < 0 || nr >= _nfields) return NULL;
+ for (i=0; i < nr; i++) {
+ if (_null & (1 <<i)) continue;
+ c += strlen(c) + 1;
+ }
+ return c;
+}
+
+char *PGconnection::getColumn(int n)
+{
+ char *c;int i;
+ if (!(result_status & PG_RSTAT_HAVE_COLUMNS)) return NULL;
+ if (n < 0 || n >= _nfields) return NULL;
+ for (c = Buffer, i = 0; i<n; i++) {
+ c += strlen(c) + 1;
+ }
+ return c;
+}
+
+char *PGconnection::getMessage(void)
+{
+ if (!(result_status & PG_RSTAT_HAVE_MESSAGE)) return NULL;
+ return Buffer;
+}
+
+int PGconnection::getData(void)
+{
+ char id;
+ int32_t msgLen;
+ int rc;
+ char *c;
+#ifndef ESP32_IDF
+ if (!client -> available()) return 0;
+#else
+ if (dataAvailable() == 0) return 0;
+#endif
+
+
+ if (pqGetc(&id)) goto read_error;
+ if (pqGetInt4(&msgLen)) goto read_error;
+ //Serial.printf("ID=%c\n", id);
+ msgLen -= 4;
+ switch(id) {
+ case 'T':
+ if ((rc=pqGetRowDescriptions())) {
+ if (rc == -2) setMsg_P(EM_OOM, PG_RSTAT_HAVE_ERROR);
+ else if (rc == -3) setMsg_P(EM_BIN, PG_RSTAT_HAVE_ERROR);
+ goto read_error;
+ }
+ if (_flags & PG_FLAG_IGNORE_COLUMNS) {
+ result_status &= ~PG_RSTAT_HAVE_MASK;
+ return 0;
+ }
+ return result_status = (result_status & ~PG_RSTAT_HAVE_MASK) | PG_RSTAT_HAVE_COLUMNS;
+
+ case 'E':
+ if (pqGetNotice(PG_RSTAT_HAVE_ERROR)) goto read_error;
+ return result_status;
+
+ case 'N':
+ if (_flags & PG_FLAG_IGNORE_NOTICES) {
+ if (pqSkipnchar(msgLen)) goto read_error;
+ return 0;
+ }
+ if(pqGetNotice(PG_RSTAT_HAVE_NOTICE)) goto read_error;
+ return result_status = (result_status & ~PG_RSTAT_HAVE_MASK) | PG_RSTAT_HAVE_NOTICE;
+
+ case 'A':
+ if (_flags & PG_FLAG_IGNORE_NOTICES) {
+ if (pqSkipnchar(msgLen)) goto read_error;
+ return 0;
+ }
+ if (pqGetNotify(msgLen)) goto read_error;
+ return result_status = (result_status & ~PG_RSTAT_HAVE_MASK) | PG_RSTAT_HAVE_NOTICE;
+
+ case 'Z':
+ if (pqSkipnchar(msgLen)) goto read_error;
+ result_status = (result_status & PG_RSTAT_HAVE_SUMMARY) | PG_RSTAT_READY;
+ return PG_RSTAT_READY;
+
+ case 'S': // parameters setting ignored
+ case 'K': // should not be here?
+ if (pqSkipnchar(msgLen)) goto read_error;
+ return 0;
+
+ case 'C': // summary
+ if (msgLen > bufSize - 1) goto oom;
+ if (pqGetnchar(Buffer, msgLen)) goto read_error;
+ Buffer[msgLen] = 0;
+ _ntuples = 0;
+ result_status = (result_status & ~PG_RSTAT_HAVE_MASK) | PG_RSTAT_HAVE_SUMMARY;
+ for (c = Buffer; *c && !isdigit(*c); c++);
+ if (!*c) return result_status;
+ if (strncmp(Buffer,"SELECT ",7)) {
+ for (; *c && isdigit(*c); c++);
+ for (; *c && !isdigit(*c); c++);
+ }
+ if (*c) _ntuples = strtol(c, NULL, 10);
+ return result_status;
+
+ case 'D':
+ if ((rc=pqGetRow())) {
+ if (rc == -2) setMsg_P(EM_OOM, PG_RSTAT_HAVE_ERROR);
+ else if (rc == -3) setMsg_P(EM_SYNC, PG_RSTAT_HAVE_ERROR);
+ goto read_error;
+ }
+ if (_flags & PG_FLAG_IGNORE_COLUMNS) {
+ result_status &= ~PG_RSTAT_HAVE_MASK;
+ return 0;
+ }
+ return result_status = (result_status & ~PG_RSTAT_HAVE_MASK) | PG_RSTAT_HAVE_ROW;
+
+ case 'I':
+ if (pqSkipnchar(msgLen)) goto read_error;
+ setMsg_P(EM_EMPTY, PG_RSTAT_HAVE_ERROR);
+ return result_status;
+
+ default:
+ setMsg_P(EM_SYNC, PG_RSTAT_HAVE_ERROR);
+ conn_status = CONNECTION_BAD;
+ return -1;
+ }
+
+oom:
+ setMsg_P(EM_OOM, PG_RSTAT_HAVE_ERROR);
+
+read_error:
+ if (!(result_status & PG_RSTAT_HAVE_ERROR)) {
+ setMsg_P(EM_READ, PG_RSTAT_HAVE_ERROR);
+ }
+ conn_status = CONNECTION_BAD;
+ return -1;
+}
+
+int PGconnection::executeFormat(int progmem, const char *format, ...)
+{
+ int32_t msgLen;
+ va_list va;
+ va_start(va, format);
+ msgLen = writeFormattedQuery(0, progmem, format, va);
+ va_end(va);
+ if (msgLen < 0) return -1;
+ va_start(va, format);
+ msgLen = writeFormattedQuery(msgLen, progmem, format, va);
+ va_end(va);
+ if (msgLen) {
+ return -1;
+ }
+ result_status = PG_RSTAT_COMMAND_SENT;
+ return 0;
+}
+
+#ifdef ESP8266
+
+// there is no strchr_P in ESP8266 ROM :(
+
+static const char *strchr_P(const char *str, char c)
+{
+ char z;
+ for (;;) {
+ z = pgm_read_byte(str);
+ if (!z) return NULL;
+ if (z == c) return str;
+ str++;
+ }
+}
+
+#endif
+
+#ifdef ESP32_IDF
+//emulate Arduino PSTR()
+char *PSTR(char *str)
+{
+ return str;
+}
+const char *PSTR(const char *str)
+{
+ return str;
+}
+
+//emulate strcpy_P()
+char *strcpy_P(char * destination, const char * source )
+{
+ return strcpy(destination, source);
+}
+
+int strlen_P(char *str)
+{
+ return strlen(str);
+}
+int strlen_P(const char *str)
+{
+ return strlen(str);
+}
+#endif
+
+int PGconnection::build_startup_packet(
+ char *packet,
+ const char *db,
+ const char *charset)
+{
+ int packet_len = 4;
+ if (packet) {
+ memcpy(packet,"\0\003\0\0", 4);
+ }
+#define ADD_STARTUP_OPTION(optname, optval) \
+ do { \
+ if (packet) \
+ strcpy_P(packet + packet_len, (char *)optname); \
+ packet_len += strlen_P((char *)optname) + 1; \
+ if (packet) \
+ strcpy(packet + packet_len, (char *)optval); \
+ packet_len += strlen((char *)optval) + 1; \
+ } while(0)
+
+#define ADD_STARTUP_OPTION_P(optname, optval) \
+ do { \
+ if (packet) \
+ strcpy_P(packet + packet_len, (char *)optname); \
+ packet_len += strlen_P((char *)optname) + 1; \
+ if (packet) \
+ strcpy_P(packet + packet_len, (char *)optval); \
+ packet_len += strlen_P((char *)optval) + 1; \
+ } while(0)
+
+ if (_user && _user[0])
+ ADD_STARTUP_OPTION(PSTR("user"), _user);
+ if (db && db[0])
+ ADD_STARTUP_OPTION(PSTR("database"), db);
+ if (charset && charset[0])
+ ADD_STARTUP_OPTION(PSTR("client_encoding"), charset);
+ ADD_STARTUP_OPTION_P(PSTR("application_name"), PSTR("arduino"));
+#undef ADD_STARTUP_OPTION
+ if (packet)
+ packet[packet_len] = '\0';
+ packet_len++;
+
+ return packet_len;
+}
+
+int PGconnection::pqPacketSend(char pack_type, const char *buf, int buf_len, int progmem)
+{
+ char *start = Buffer;
+ int l = bufSize - 4;
+#ifndef ESP32
+ int n;
+#endif
+ if (pack_type) {
+ *start++ = pack_type;
+ l--;
+ }
+#ifdef __AVR__
+ *start++=0;
+ *start++=0;
+#else
+ *start++ = ((buf_len + 4) >> 24) & 0xff;
+ *start++ = ((buf_len + 4) >> 16) & 0xff;
+#endif
+ *start++ = ((buf_len + 4) >> 8) & 0xff;
+ *start++ = (buf_len + 4) & 0xff;
+#ifndef ESP32
+ if (progmem) {
+ while (buf_len > 0) {
+ while (buf_len > 0 && l > 0) {
+ *start++ = pgm_read_byte(buf++);
+ buf_len--;
+ }
+ n = client->write((const uint8_t *)Buffer, start - Buffer);
+ if (n != start - Buffer) return -1;
+ start = Buffer;
+ l = bufSize;
+ }
+ }
+ else {
+#endif
+ if (buf) {
+ if (buf_len <= l) {
+ memcpy(start, buf, buf_len);
+ start += buf_len;
+ buf_len = 0;
+ }
+ else {
+ memcpy(start, buf, l);
+ start += l;
+ buf_len -= l;
+ buf += l;
+ }
+ }
+#ifndef ESP32_IDF
+ n = client->write((const uint8_t *)Buffer, start - Buffer);
+ if (n != start - Buffer) return -1;
+ if (buf && buf_len) {
+ n = client->write((const uint8_t *)buf, buf_len);
+ if (n != buf_len) return -1;
+ }
+#else
+ int err = send(SockH, /*_buffer*/Buffer, start - /*_buffer*/Buffer, 0);
+ if (err < 0) {
+ ESP_LOGE(PGTAG, "pqPacketSend: Send Error occurred during sending: errno %d", errno);
+ return err;
+ }
+ if (buf && buf_len) {
+ err = send(SockH, (const char *) buf, (size_t) buf_len, 0);
+ if (err < 0) {
+ ESP_LOGE(PGTAG, "Send2 Error occurred during sending: errno %d", errno);
+ return err;
+
+ }
+ }
+#endif
+
+#ifndef ESP32
+ }
+#endif
+ return 0;
+}
+
+int PGconnection::pqGetc(char *buf)
+{
+ int i;
+#ifndef ESP32_IDF
+ for (i=0; !client->available() && i < 10; i++) {
+ delay (i * 10 + 10);
+ }
+ if (!client->available()) {
+ return -1;
+ }
+ *buf = client->read();
+ return 0;
+#else
+ i = 0;
+ while (i<10) {
+ if (dataAvailable()>0) break;
+ else {
+ vTaskDelay(i++);
+ }
+ }
+ if (i==10) return -1;
+
+ int len = read(SockH, (void *) buf, 1);
+ _available-=len;
+ return -1+len;
+#endif
+}
+
+int PGconnection::pqGetInt4(int32_t *result)
+{
+ uint32_t tmp4 = 0;
+ byte tmp,i;
+ for (i = 0; i < 4; i++) {
+ if (pqGetc((char *)&tmp)) return -1;
+ tmp4 = (tmp4 << 8) | tmp;
+ }
+ *result = tmp4;
+ return 0;
+}
+
+int PGconnection::pqGetInt2(int16_t *result)
+{
+ uint16_t tmp2 = 0;
+ byte tmp,i;
+ for (i = 0; i < 2; i++) {
+ if (pqGetc((char *)&tmp)) return -1;
+ tmp2 = (tmp2 << 8) | tmp;
+ }
+ *result = tmp2;
+ return 0;
+}
+
+int PGconnection::pqGetnchar(char *s, int len)
+{
+ while (len-- > 0) {
+ if (pqGetc(s++)) return -1;
+ }
+ return 0;
+}
+
+int PGconnection::pqGets(char *s, int maxlen)
+{
+ int len;
+ char z;
+ for (len = 0;len < maxlen; len++) {
+ if (pqGetc(&z)) return -1;
+ if (s) *s++ = z;
+ if (!z) return len+1;
+ }
+ return - (len + 1);
+}
+
+int PGconnection::pqSkipnchar(int len)
+{
+ char dummy;
+ while (len-- > 0) {
+ if (pqGetc(&dummy)) return -1;
+ }
+ return 0;
+}
+
+int PGconnection::pqGetRow(void)
+{
+ int i;
+ int bufpos = 0;
+ int32_t len;
+ int16_t cols;
+
+ _null = 0;
+ if (pqGetInt2(&cols)) return -1;
+ if (cols != _nfields) {
+ return -3;
+ }
+ for (i=0; i < _nfields; i++) {
+ if (pqGetInt4(&len)) return -1;
+ if (len < 0) {
+ _null |= 1<<i;
+ continue;
+ }
+ if (bufpos + len + 1 > bufSize) {
+ return -2;
+ }
+ if (pqGetnchar(Buffer + bufpos, len)) return -1;
+ bufpos += len;
+ Buffer[bufpos++]=0;
+ }
+ return 0;
+}
+
+
+int PGconnection::pqGetRowDescriptions(void)
+{
+ int i;
+ int16_t format;
+ int rc;
+ int bufpos;
+ if (pqGetInt2(&_nfields)) return -1;
+ if (_nfields > PG_MAX_FIELDS) return -2; // implementation limit
+ _formats = 0;
+ bufpos = 0;
+ for (i = 0;i < _nfields; i++) {
+ if (!(_flags & PG_FLAG_IGNORE_COLUMNS)) {
+ if (bufpos >= bufSize - 1) return -2;
+ rc = pqGets(Buffer + bufpos, bufSize - bufpos);
+ if (rc < 0) return -1;
+ bufpos += rc;
+ }
+ else {
+ if (pqGets(NULL, 8192) < 0) {
+ return -1;
+ }
+ }
+ if (pqSkipnchar(16)) return -1;
+ if (pqGetInt2(&format)) return -1;
+ format = format ? 1 : 0;
+ _formats |= format << i;
+ }
+ if (_formats) return -3;
+ return 0;
+}
+
+void PGconnection::setMsg(const char *s, int type)
+{
+ strcpy(Buffer, s);
+ result_status = (result_status & ~PG_RSTAT_HAVE_MASK) | type;
+}
+
+void PGconnection::setMsg_P(const char *s, int type)
+{
+ strcpy_P(Buffer, s);
+ result_status = (result_status & ~PG_RSTAT_HAVE_MASK) | type;
+}
+
+int PGconnection::pqGetNotice(int type)
+{
+ int bufpos = 0;
+ char id;
+ int rc;
+ for (;;) {
+ if (pqGetc(&id)) goto read_error;
+ if (!id) break;
+ if (id == 'S' || id == 'M') {
+ if (bufpos && bufpos < bufSize - 1) Buffer[bufpos++]=':';
+ rc = pqGets(Buffer + bufpos, bufSize - bufpos);
+ if (rc < 0) goto read_error;
+ bufpos += rc -1;
+ }
+ else {
+ rc = pqGets(NULL, 8192);
+ if (rc < 0) goto read_error;
+ }
+ }
+ Buffer[bufpos] = 0;
+ result_status = (result_status & ~PG_RSTAT_HAVE_MASK) | type;
+ return 0;
+
+read_error:
+ if (!bufpos) setMsg_P(EM_READ, PG_RSTAT_HAVE_ERROR);
+ return -1;
+}
+
+int PGconnection::pqGetNotify(int32_t msgLen)
+{
+ int32_t pid;
+ int bufpos, i;
+ if (pqGetInt4(&pid)) return -1;
+ msgLen -= 4;
+ bufpos = sprintf(Buffer,"%d:",pid);
+ if (msgLen > bufSize - (bufpos + 1)) {
+ if (pqGetnchar(Buffer+bufpos, bufSize - (bufpos + 1)))
+ return -1;
+ msgLen -= bufSize - (bufpos + 1);
+ if (pqSkipnchar(msgLen)) return -1;
+ Buffer[msgLen = bufSize - 1] = 0;
+
+ }
+ else {
+ if (pqGetnchar(Buffer+ bufpos, msgLen)) return -1;
+ Buffer[bufpos + msgLen] = 0;
+ msgLen += bufpos;
+ }
+ for (i=0; i<msgLen; i++) if (!Buffer[i]) Buffer[i] = ':';
+ return 0;
+}
+
+#ifndef ESP32
+int PGconnection::writeMsgPart_P(const char *s, int len, int fine)
+{
+ while (len > 0) {
+ if (bufPos >= bufSize) {
+ if (client->write((uint8_t *)Buffer, bufPos) != (size_t)bufPos) {
+ return -1;
+ }
+ bufPos = 0;
+ }
+ Buffer[bufPos++] = pgm_read_byte(s++);
+ len--;
+ }
+ if (bufPos && fine) {
+ if (client->write((uint8_t *)Buffer, bufPos) != (size_t)bufPos) {
+ return -1;
+ }
+ bufPos = 0;
+ }
+ return 0;
+}
+#endif
+
+int PGconnection::writeMsgPart(const char *s, int len, int fine)
+{
+ while (len > 0) {
+ int n = len;
+ if (n > bufSize - bufPos) n = bufSize - bufPos;
+ memcpy(Buffer + bufPos, s, n);
+ bufPos += n;
+ s += n;
+ len -= n;
+ if (bufPos >= bufSize) {
+#ifndef ESP32_IDF
+ if (client->write((uint8_t *)Buffer, bufPos) != (size_t)bufPos) {
+ return -1;
+ }
+#else
+ int err = send(SockH, /*_buffer*/Buffer, bufPos, 0);
+ if (err < 0)
+ return -1;
+#endif
+
+ bufPos = 0;
+ }
+ }
+ if (bufPos && fine) {
+#ifndef ESP32_IDF
+ if (client->write((uint8_t *)Buffer, bufPos) != (size_t)bufPos) {
+ return -1;
+ }
+#else
+ int err = send(SockH, /*_buffer*/Buffer, bufPos, 0);
+ if (err < 0)
+ return -1;
+#endif
+ bufPos = 0;
+ }
+
+ return 0;
+}
+
+int32_t PGconnection::writeFormattedQuery(int32_t length, int progmem, const char *format, va_list va)
+{
+ int32_t msgLen = 0;
+ const char *percent;
+ int blen, rc;
+ char buf[32], znak;
+#ifdef ESP32
+ (void) progmem;
+#endif
+ if (length) {
+ length += 4;
+ bufPos = 0;
+ Buffer[bufPos++] = 'Q';
+ Buffer[bufPos++] = (length >> 24) & 0xff;
+ Buffer[bufPos++] = (length >> 16) & 0xff;
+ Buffer[bufPos++] = (length >> 8) & 0xff;
+ Buffer[bufPos++] = (length) & 0xff;
+ }
+ for (;;) {
+#ifndef ESP32
+ if (progmem) {
+ percent = strchr_P(format, '%');
+ }
+ else {
+#endif
+ percent = strchr(format, '%');
+#ifndef ESP32
+ }
+#endif
+ if (!percent) break;
+#ifndef ESP32
+ if (progmem) {
+ znak = pgm_read_byte(percent+1);
+ }
+ else {
+#endif
+ znak = percent[1];
+#ifndef ESP32
+ }
+#endif
+ if (!length) {
+ msgLen += (percent - format);
+ }
+ else {
+#ifndef ESP32
+ if (progmem) {
+ rc = writeMsgPart_P(format, percent - format, false);
+ }
+ else {
+#endif
+ rc = writeMsgPart(format, percent - format, false);
+#ifndef ESP32
+ }
+#endif
+ if (rc) goto write_error;
+ }
+ format = percent + 2;
+ if (znak == 's' || znak == 'n') {
+ char *str = va_arg(va, char *);
+ blen = (znak == 's') ? escapeString(str, NULL) : escapeName(str, NULL);
+ if (!length) {
+ msgLen += blen;
+ }
+ else {
+ if (bufPos + blen > bufSize) {
+ rc = writeMsgPart(NULL, 0, true);
+ if (rc) goto write_error;
+ }
+ }
+ if (znak == 's') {
+ escapeString(str, Buffer + bufPos);
+ }
+ else {
+ escapeName(str, Buffer + bufPos);
+ }
+ bufPos += blen;
+ continue;
+ }
+ if (znak == 'l' || znak == 'd') {
+ if (znak == 'l') {
+ long n = va_arg(va, long);
+ blen = snprintf(buf, 32, "'%ld'", n);
+ }
+ else {
+ int n = va_arg(va, int);
+ blen = snprintf(buf, 32, "'%d'", n);
+ }
+ if (length) {
+ rc = writeMsgPart(buf, blen, false);
+ if (rc) goto write_error;
+ }
+ else {
+ msgLen += blen;
+ }
+ }
+ setMsg_P(EM_FORMAT, PG_RSTAT_HAVE_ERROR);
+ return -1;
+ }
+#ifndef ESP32
+ if (progmem) {
+ blen = strlen_P(format);
+ }
+ else {
+#endif
+ blen = strlen(format);
+#ifndef ESP32
+ }
+#endif
+ if (length) {
+#ifndef ESP32
+ if (progmem) {
+ rc = writeMsgPart_P(format, blen, false);
+ }
+ else {
+#endif
+ rc = writeMsgPart(format, blen, false);
+#ifndef ESP32
+ }
+#endif
+ if (!rc) {
+ rc = writeMsgPart("\0",1,true);
+ }
+ if (rc) goto write_error;
+ }
+ else {
+ msgLen += blen + 1;
+ }
+ return msgLen;
+write_error:
+ setMsg_P(EM_WRITE, PG_RSTAT_HAVE_ERROR);
+ conn_status = CONNECTION_BAD;
+ return -1;
+}
diff --git a/software/michi_funcs/SimplePgSQL.h b/software/michi_funcs/SimplePgSQL.h
new file mode 100644
index 0000000..383c899
--- /dev/null
+++ b/software/michi_funcs/SimplePgSQL.h
@@ -0,0 +1,278 @@
+/*
+ * SimplePgSQL.h - Lightweight PostgreSQL connector for Arduino
+ * Copyright (C) Bohdan R. Rau 2016 <ethanak@polip.com>
+ *
+ * SimplePgSQL is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * SimplePgSQL is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with SimplePgSQL. If not, write to:
+ * The Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301, USA.
+ */
+#ifndef _SIMPLEPGSQL
+#define _SIMPLEPGSQL 1
+
+//Michi begin
+#define ESP32
+#define ESP32_IDF
+//Michi end
+
+#ifdef __AVR__
+// You need MD5 library from https://github.com/tzikis/ArduinoMD5
+// for Arduino boards. Uncomment only if you have this library and
+// you must use md5 passwords.
+// Do not use it on 32 kB ATMega processors!
+
+// #define PG_USE_MD5 1
+
+#else
+// ESP8266 has MD5 code in ROM so there is no need to comment
+#define PG_USE_MD5 1
+#endif
+
+
+
+
+#ifndef ESP32_IDF
+ #include <Arduino.h>
+ #include <Client.h>
+#else
+ #include "esp_system.h"
+ #include "esp_netif.h"
+ #include "lwip/err.h"
+ #include "lwip/sockets.h"
+
+ #define byte uint8_t
+#endif
+
+typedef enum
+{
+ CONNECTION_OK,
+ CONNECTION_BAD,
+ CONNECTION_NEEDED, /* setDbLogin() needed */
+ /* Internal states here */
+ CONNECTION_AWAITING_RESPONSE, /* Waiting for a response from the
+ * postmaster. */
+ CONNECTION_AUTH_OK /* Received authentication; waiting for
+ * backend startup. */
+} ConnStatusType;
+
+#ifdef ESP8266
+#define PG_BUFFER_SIZE 2048
+#elif defined(ESP32)
+#define PG_BUFFER_SIZE 16384
+#else
+#define PG_BUFFER_SIZE 256
+#endif
+
+// maximum number of fields in backend response
+// must not exceed number of bits in _formats and _null
+#ifdef ESP32
+#define PG_MAX_FIELDS 64
+#else
+#define PG_MAX_FIELDS 32
+#endif
+// ignore notices and notifications
+#define PG_FLAG_IGNORE_NOTICES 1
+// do not store column names
+#define PG_FLAG_IGNORE_COLUMNS 2
+// never set this flag manually!
+# define PG_FLAG_STATIC_BUFFER 4
+
+// ready for next query
+#define PG_RSTAT_READY 1
+// command sent
+#define PG_RSTAT_COMMAND_SENT 2
+// column names in buffer
+#define PG_RSTAT_HAVE_COLUMNS 4
+// row values in buffer
+#define PG_RSTAT_HAVE_ROW 8
+// summary (number of tuples/affected rows) received
+#define PG_RSTAT_HAVE_SUMMARY 16
+// error message in buffer
+#define PG_RSTAT_HAVE_ERROR 32
+// notice/notification in buffer
+#define PG_RSTAT_HAVE_NOTICE 64
+
+#define PG_RSTAT_HAVE_MASK (PG_RSTAT_HAVE_COLUMNS | \
+ PG_RSTAT_HAVE_ROW | \
+ PG_RSTAT_HAVE_SUMMARY | \
+ PG_RSTAT_HAVE_ERROR | \
+ PG_RSTAT_HAVE_NOTICE)
+
+#define PG_RSTAT_HAVE_MESSAGE (PG_RSTAT_HAVE_ERROR | PG_RSTAT_HAVE_NOTICE)
+
+// Framework abstraction - Michi
+// only here, differences of Arduino, ESP32-Arduino, ESP32-IDF or Platform.io should be made
+//class fw_function {
+// public:
+// send_network_packet();
+//}
+
+
+class PGconnection {
+ public:
+#ifndef ESP32_IDF
+ PGconnection(Client *c, int flags = 0, int memory = 0, char *foreignBuffer = NULL);
+#else
+ PGconnection(const int flags, const unsigned char *_Buffer, const int bufSize);
+#endif
+ /*
+ * returns connection status.
+ * passwd may be null in case of 'trust' authorization.
+ * only 'trust', 'password' and 'md5' (if compiled in)
+ * authorization modes are implemented.
+ * ssl mode is not implemented.
+ * database name defaults to user name *
+ */
+#ifndef ESP32_IDF
+ int setDbLogin(IPAddress server, const char *user, const char *passwd = NULL, const char *db = NULL, const char *charset = NULL, int port = 5432);
+#else
+ int setDbLogin(const char *ServerIP, int ServerPort, const char *dbName, const char *dbUser, const char *dbPasswd, const char *charset);
+#endif
+ /*
+ * performs authorization tasks if needed
+ * returns current connection status
+ * must be called periodically until OK, BAD or NEEDED
+ */
+ int status(void);
+ /*
+ * sends termination command if possible
+ * closes client connection and frees internal buffer
+ */
+ void close(void);
+ /*
+ * sends query to backend
+ * returns negative value on error
+ * or zero on success
+ */
+ int execute(const char *query, int progmem = 0);
+ int PGexecute(const char *query); //old version from 17.02.2022
+
+ /* should be called periodically in idle state
+ * if notifications are enabled
+ * returns:
+ * - negative value on error
+ * - zero if no interesting data arrived
+ * - current data status if some data arrived
+ */
+
+ int getData(void);
+ /*
+ * returns pointer to n-th column name in internal buffer
+ * if available or null if column number out of range
+ * will be invalidated on next getData call
+ */
+ char *getColumn(int n);
+ /*
+ * returns pointer to n-th column value in internal buffer
+ * if available or null if column number out of range
+ * or value is NULL
+ * will be invalidated on next getData call
+ */
+ char *getValue(int);
+ /*
+ * returns pointer to message (error or notice)
+ * if available or NULL
+ * will be invalidated on next getData call
+ */
+ char *getMessage(void);
+ int dataStatus(void) {
+ return result_status;
+ };
+ int nfields(void) {
+ return _nfields;
+ };
+ int ntuples(void) {
+ return _ntuples;
+ };
+ /*
+ * returns length of escaped string
+ * single quotes and E prefix (if needed)
+ * will be added.
+ */
+ int escapeString(const char *inbuf, char *outbuf);
+ /*
+ * returns length of escaped string
+ * double quotes will be added.
+ */
+ int escapeName(const char *inbuf, char *outbuf);
+ /*
+ * sends formatted query to backend
+ * returns negative value on error
+ * or zero on success
+ * Formatting sequences:
+ * %s - string literal (will be escaped with escapeString)
+ * %n - name (will be escaped with escapeName)
+ * %d - int (single quotes will be added)
+ * %l - long int (single quotes will be added)
+ * %% - % character
+ */
+ int executeFormat(int progmem, const char *format, ...);
+
+
+ private:
+#ifndef ESP32_IDF
+ Client *client;
+#endif
+ int pqPacketSend(char pack_type, const char *buf, int buf_len, int progmem = 0);
+ int pqGetc(char *);
+ int pqGetInt4(int32_t *result);
+ int pqGetInt2(int16_t *result);
+ int pqGetnchar(char *s, int len);
+ int pqSkipnchar(int len);
+ int pqGets(char *s, int maxlen);
+ int pqGetRowDescriptions(void);
+ int pqGetRow(void);
+ void setMsg(const char *, int);
+ void setMsg_P(const char *, int);
+ int pqGetNotice(int);
+ int pqGetNotify(int32_t);
+ char *_user;
+ char *_passwd;
+ char *Buffer;
+ int bufSize;
+ int bufPos;
+ int writeMsgPart(const char *s, int len, int fine);
+ int writeMsgPart_P(const char *s, int len, int fine);
+ int32_t writeFormattedQuery(int32_t length, int progmem, const char *format, va_list va);
+
+ int build_startup_packet(char *packet, const char *db, const char *charset);
+ byte conn_status;
+ byte attempts;
+/*
+ int32_t be_pid;
+ int32_t be_key;
+*/
+ int16_t _nfields;
+ int16_t _ntuples;
+#ifdef ESP32_IDF
+ uint64_t _formats;
+ uint64_t _null;
+ uint32_t _available; //michi
+ struct sockaddr_in DestAddr;
+ int SockH = -1;
+ int ipProtocol = 0;
+ int AddrFamily = 0;
+ int NetConnected=0;
+
+ int dataAvailable(void);
+#else
+ uint32_t _formats;
+ uint32_t _null;
+#endif
+ byte _binary;
+ byte _flags;
+ int result_status;
+};
+
+#endif
diff --git a/software/michi_funcs/SimplePgSQL_cpp.old b/software/michi_funcs/SimplePgSQL_cpp.old
new file mode 100644
index 0000000..0e76e0a
--- /dev/null
+++ b/software/michi_funcs/SimplePgSQL_cpp.old
@@ -0,0 +1,900 @@
+/*
+ SimplePgSQL.c - Lightweight PostgreSQL connector for Arduino
+ Copyright (C) Bohdan R. Rau 2016 <ethanak@polip.com>
+
+ SimplePgSQL is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ SimplePgSQL is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with SimplePgSQL. If not, write to:
+ The Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor
+ Boston, MA 02110-1301, USA.
+ */
+#include <stdio.h>
+//#include "esp_eth.h"
+#include <string.h>
+#include <sys/param.h>
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "freertos/event_groups.h"
+#include "esp_system.h"
+#include "esp_wifi.h"
+#include "esp_event.h"
+#include "esp_log.h"
+#include "nvs_flash.h"
+#include "esp_netif.h"
+#include "lwip/err.h"
+#include "lwip/sockets.h"
+
+
+#include "SimplePgSQL.h"
+
+#define AUTH_REQ_OK 0 /* User is authenticated */
+#define AUTH_REQ_PASSWORD 3 /* Password */
+#define PGTAG "PGSQL"
+static const char EM_OOM[] = "Out of mem";
+static const char EM_READ[] = "Backend read err";
+static const char EM_WRITE[] = "Backend write err";
+static const char EM_CONN[] = "Cannot conn 2 srv";
+static const char EM_SYNC[] = "Backend out of sync";
+static const char EM_INTR[] = "Internal err";
+static const char EM_UAUTH[] = "auth method !sptd";
+static const char EM_BIN[] = "Bin fmt !sptd";
+static const char EM_EXEC[] = "Previ exe !finished";
+static const char EM_PASSWD[] = "Pwd req";
+static const char EM_EMPTY[] = "empty qry";
+static const char EM_FORMAT[] = "Illegal chr fmt";
+
+PGconnection::PGconnection(const int flags, const unsigned char *Buffer, const int bufSize) {
+ conn_status = CONNECTION_NEEDED;
+ _passwd = NULL;
+ _user = NULL;
+ _buffer = (char *) Buffer;
+ _bufSize = bufSize;
+ bufPos = 0;
+ result_status=0;
+ _available=0;
+ _nfields=0;
+ _ntuples=0;
+ _flags=0;
+}
+
+int PGconnection::PGsetDbLogin(const char *ServerIP, int ServerPort, const char *dbName, const char *dbUser, const char *dbPasswd, const char *dbCharset) {
+
+ char *startpacket;
+ int packetlen;
+ int len;
+
+// close();
+ memset(&DestAddr, 0, sizeof(DestAddr));
+ AddrFamily = AF_INET;
+ ipProtocol = IPPROTO_IP;
+ DestAddr.sin_addr.s_addr = inet_addr(ServerIP);
+ DestAddr.sin_family = AF_INET;
+ DestAddr.sin_port = htons(ServerPort);
+
+ if (!dbName)
+ dbName = dbUser;
+ len = strlen(dbUser) + 1;
+ if (dbPasswd) {
+ len += strlen(dbPasswd) + 1;
+ }
+ _user = (char *) malloc(len);
+ strcpy(_user, dbUser);
+ if (dbPasswd) {
+ _passwd = _user + strlen(dbUser) + 1;
+ strcpy(_passwd, dbPasswd);
+ } else {
+ _passwd = NULL;
+ }
+
+ //int8_t connected = connect(client->connect(server, port);
+ SockH = socket(AddrFamily, SOCK_STREAM, ipProtocol);
+ if (SockH < 0) {
+ ESP_LOGE(PGTAG, "Unable to create socket: errno %d", errno);
+ setMsg_P(EM_CONN, PG_RSTAT_HAVE_ERROR);
+ return conn_status = CONNECTION_BAD;
+ } else {
+ int err = connect(SockH, (struct sockaddr *) &DestAddr, sizeof(DestAddr));
+ if (err != 0) {
+ ESP_LOGE(PGTAG, "Socket unable to connect: errno %d", errno);
+ return conn_status = CONNECTION_BAD;
+ }
+ NetConnected = 1;
+ ESP_LOGI(PGTAG, "Successfully connected");
+ }
+
+ packetlen = build_startup_packet(NULL, dbName, dbCharset);
+ if (packetlen > _bufSize - 10) {
+ setMsg_P(EM_OOM, PG_RSTAT_HAVE_ERROR);
+ conn_status = CONNECTION_BAD;
+ return conn_status;
+ }
+
+ startpacket = _buffer + (_bufSize - (packetlen + 1));
+ build_startup_packet(startpacket, dbName, dbCharset);
+ if (pqPacketSend(0, startpacket, packetlen) < 0) {
+ setMsg_P(EM_WRITE, PG_RSTAT_HAVE_ERROR);
+ return conn_status = CONNECTION_BAD;
+ }
+ attempts = 0;
+ return conn_status = CONNECTION_AWAITING_RESPONSE;
+}
+
+void PGconnection::PGclose(void) {
+ if (NetConnected) {
+ pqPacketSend('X', NULL, 0);
+// client->stop();
+ shutdown(SockH, 0);
+ close(SockH);
+ }
+ if (_user) {
+ free(_user);
+ _user = _passwd = NULL;
+ }
+ NetConnected = 0;
+ conn_status = CONNECTION_NEEDED;
+}
+
+int PGconnection::PGstatus(void) {
+ char bereq;
+ char rc;
+ int32_t msgLen;
+ int32_t areq;
+ char * pwd = _passwd;
+
+ switch (conn_status) {
+ case CONNECTION_NEEDED:
+ case CONNECTION_OK:
+ case CONNECTION_BAD:
+
+ return conn_status;
+
+ case CONNECTION_AWAITING_RESPONSE:
+ if (dataAvailable() == 0) return conn_status;
+ if (attempts++ >= 2) {
+ setMsg_P(EM_SYNC, PG_RSTAT_HAVE_ERROR);
+ return conn_status = CONNECTION_BAD;
+ }
+ if (pqGetc(&bereq)) {
+ goto read_error;
+ }
+ if (bereq == 'E') {
+ pqGetInt4(&msgLen);
+ pqGetNotice(PG_RSTAT_HAVE_ERROR);
+ return conn_status = CONNECTION_BAD;
+ }
+ if (bereq != 'R') {
+ setMsg_P(EM_SYNC, PG_RSTAT_HAVE_ERROR);
+ return conn_status = CONNECTION_BAD;
+ }
+ if (pqGetInt4(&msgLen)) {
+ goto read_error;
+ }
+ if (pqGetInt4(&areq)) {
+ goto read_error;
+ }
+ if (areq == AUTH_REQ_OK) {
+ if (_user) {
+ free(_user);
+ _user = _passwd = NULL;
+ }
+ result_status = PG_RSTAT_READY;
+ return conn_status = CONNECTION_AUTH_OK;
+ }
+ if (areq != AUTH_REQ_PASSWORD) {
+ setMsg_P(EM_UAUTH, PG_RSTAT_HAVE_ERROR);
+ return conn_status = CONNECTION_BAD;
+ }
+ if (!_passwd || !*_passwd) {
+ setMsg_P(EM_PASSWD, PG_RSTAT_HAVE_ERROR);
+ return conn_status = CONNECTION_BAD;
+ }
+ pwd = _passwd;
+ rc = pqPacketSend('p', pwd, strlen(pwd) + 1);
+ if (rc) {
+ goto write_error;
+ }
+ return conn_status;
+
+ case CONNECTION_AUTH_OK:
+ for (;;) {
+ if (dataAvailable() == 0) return conn_status;
+ if (pqGetc(&bereq))
+ goto read_error;
+ if (pqGetInt4(&msgLen))
+ goto read_error;
+ msgLen -= 4;
+ if (bereq == 'A' || bereq == 'N' || bereq == 'S' || bereq == 'K') {
+ if (pqSkipnchar(msgLen))
+ goto read_error;
+ continue;
+ }
+ if (bereq == 'E') {
+ pqGetNotice(PG_RSTAT_HAVE_ERROR);
+ return conn_status = CONNECTION_BAD;
+ }
+
+ /* if (bereq == 'K') {
+ if (pqGetInt4(&be_pid)) goto read_error;
+ if (pqGetInt4(&be_key)) goto read_error;
+ continue;
+ }
+ */
+ if (bereq == 'Z') {
+ pqSkipnchar(msgLen);
+ return conn_status = CONNECTION_OK;
+ }
+ return conn_status = CONNECTION_BAD;
+ }
+ break;
+ default:
+ setMsg_P(EM_INTR, PG_RSTAT_HAVE_ERROR);
+ return conn_status = CONNECTION_BAD;
+ }
+ read_error: setMsg_P(EM_READ, PG_RSTAT_HAVE_ERROR);
+ return conn_status = CONNECTION_BAD;
+ write_error: setMsg_P(EM_WRITE, PG_RSTAT_HAVE_ERROR);
+ return conn_status = CONNECTION_BAD;
+}
+
+int PGconnection::PGexecute(const char *query) {
+ /*
+ if (!(result_status & PG_RSTAT_READY)) {
+ setMsg_P(EM_EXEC, PG_RSTAT_HAVE_ERROR);
+ return -1;
+ }
+ */
+ int len = strlen(query);
+ if (pqPacketSend('Q', query, len + 1)) {
+ setMsg_P(EM_WRITE, PG_RSTAT_HAVE_ERROR);
+ conn_status = CONNECTION_BAD;
+ return -1;
+ }
+ result_status = PG_RSTAT_COMMAND_SENT;
+ return 0;
+}
+
+int PGconnection::PGescapeName(const char *inbuf, char *outbuf) {
+ const char *c;
+ int l = 2;
+ for (c = inbuf; *c; c++) {
+ l++;
+ if (*c == '\\' || *c == '"')
+ l++;
+ }
+ if (outbuf) {
+ *outbuf++ = '"';
+ for (c = inbuf; *c; c++) {
+ *outbuf++ = *c;
+ if (*c == '\\' || *c == '"')
+ *outbuf++ = *c;
+ }
+ *outbuf++ = '"';
+ }
+ return l;
+}
+
+int PGconnection::PGescapeString(const char *inbuf, char *outbuf) {
+ const char *c;
+ int e = 0, l;
+ for (c = inbuf; *c; c++) {
+ if (*c == '\\' || *c == '\'')
+ e++;
+ }
+ l = e + (c - inbuf) + (e ? 4 : 2);
+ if (outbuf) {
+ if (e) {
+ *outbuf++ = ' ';
+ *outbuf++ = 'E';
+ }
+ *outbuf++ = '\'';
+ for (c = inbuf; *c; c++) {
+ *outbuf++ = *c;
+ if (*c == '\\' || *c == '\'')
+ *outbuf++ = *c;
+ }
+ *outbuf++ = '\'';
+ }
+ return l;
+}
+
+char * PGconnection::PGgetValue(int nr) {
+ int i;
+ if (_null & (1 << nr)) return NULL;
+ char *c = _buffer;
+ if (nr < 0 || nr >= _nfields) return NULL;
+ for (i = 0; i < nr; i++) {
+ if (_null & (1 << i)) continue;
+ c += strlen(c) + 1;
+ }
+ return c;
+}
+
+char *PGconnection::PGgetColumn(int n) {
+ char *c;
+ int i;
+ if (!(result_status & PG_RSTAT_HAVE_COLUMNS)) return NULL;
+ if (n < 0 || n >= _nfields) return NULL;
+ for (c = _buffer, i = 0; i < n; i++) {
+ c += strlen(c) + 1;
+ }
+ return c;
+}
+
+char *PGconnection::PGgetMessage(void) {
+ if (!(result_status & PG_RSTAT_HAVE_MESSAGE))
+ return NULL;
+ return _buffer;
+}
+
+void dumpbuffer(char *b,int l) {
+ int i;
+ unsigned int v;
+ for (i=0;i<l;i++) {
+ if (i%8 == 0) {
+ printf("\n%04X ",i);
+ }
+ v=*b;
+ printf("%02X ",v);
+ if (v>31) printf(" %c ",v);
+ else printf(" ");
+ b++;
+ }
+ printf("\n");
+}
+
+int PGconnection::PGgetData(void) {
+ char id;
+ int32_t msgLen;
+ int rc;
+ char *c;
+ int r=0;
+ r=dataAvailable();
+// printf("getData:avail:%d\n",r);fflush(stdout);
+ if (r==0) return 0;
+
+ if (pqGetc(&id)) goto read_error;
+ if (pqGetInt4(&msgLen)) goto read_error;
+// printf("MSG ID: %c Len:%d (avail:%d)\n",id,msgLen,r);fflush(stdout);
+ msgLen -= 4;
+ switch (id) {
+ case 'T':
+ if ((rc = pqGetRowDescriptions())) {
+ if (rc == -2)
+ setMsg_P(EM_OOM, PG_RSTAT_HAVE_ERROR);
+ else if (rc == -3)
+ setMsg_P(EM_BIN, PG_RSTAT_HAVE_ERROR);
+ goto read_error;
+ }
+ if (_flags & PG_FLAG_IGNORE_COLUMNS) {
+ result_status &= ~PG_RSTAT_HAVE_MASK;
+ return 0;
+ }
+ return result_status = (result_status & ~PG_RSTAT_HAVE_MASK) | PG_RSTAT_HAVE_COLUMNS;
+
+ case 'E':
+ if (pqGetNotice(PG_RSTAT_HAVE_ERROR))
+ goto read_error;
+ return result_status;
+
+ case 'N':
+ if (_flags & PG_FLAG_IGNORE_NOTICES) {
+ if (pqSkipnchar(msgLen))
+ goto read_error;
+ return 0;
+ }
+ if (pqGetNotice(PG_RSTAT_HAVE_NOTICE))
+ goto read_error;
+ return result_status = (result_status & ~PG_RSTAT_HAVE_MASK) | PG_RSTAT_HAVE_NOTICE;
+
+ case 'A':
+ if (_flags & PG_FLAG_IGNORE_NOTICES) {
+ if (pqSkipnchar(msgLen))
+ goto read_error;
+ return 0;
+ }
+ if (pqGetNotify(msgLen))
+ goto read_error;
+ return result_status = (result_status & ~PG_RSTAT_HAVE_MASK) | PG_RSTAT_HAVE_NOTICE;
+
+ case 'Z':
+ if (pqSkipnchar(msgLen))
+ goto read_error;
+ result_status = (result_status & PG_RSTAT_HAVE_SUMMARY) | PG_RSTAT_READY;
+ return PG_RSTAT_READY;
+
+ case 'S':
+ case 'K':
+ if (pqSkipnchar(msgLen))
+ goto read_error;
+ return 0;
+
+ case 'C':
+ if (msgLen > _bufSize - 1)
+ goto oom;
+ if (pqGetnchar(_buffer, msgLen))
+ goto read_error;
+ _buffer[msgLen] = 0;
+ _ntuples = 0;
+ result_status = (result_status & ~PG_RSTAT_HAVE_MASK) | PG_RSTAT_HAVE_SUMMARY;
+ for (c = _buffer; *c && !isdigit(*c); c++)
+ ;
+ if (!*c)
+ return result_status;
+ if (strncmp(_buffer, "SELECT ", 7)) {
+ for (; *c && isdigit(*c); c++)
+ ;
+ for (; *c && !isdigit(*c); c++)
+ ;
+ }
+ if (*c)
+ _ntuples = strtol(c, NULL, 10);
+ return result_status;
+
+ case 'D':
+ if ((rc = pqGetRow())) {
+ if (rc == -2)
+ setMsg_P(EM_OOM, PG_RSTAT_HAVE_ERROR);
+ else if (rc == -3)
+ setMsg_P(EM_SYNC, PG_RSTAT_HAVE_ERROR);
+ goto read_error;
+ }
+ if (_flags & PG_FLAG_IGNORE_COLUMNS) {
+ result_status &= ~PG_RSTAT_HAVE_MASK;
+ return 0;
+ }
+
+ return result_status = (result_status & ~PG_RSTAT_HAVE_MASK) | PG_RSTAT_HAVE_ROW;
+
+ case 'I':
+ if (pqSkipnchar(msgLen))
+ goto read_error;
+ setMsg_P(EM_EMPTY, PG_RSTAT_HAVE_ERROR);
+ return result_status;
+
+ default:
+ setMsg_P(EM_SYNC, PG_RSTAT_HAVE_ERROR);
+ conn_status = CONNECTION_BAD;
+ return -1;
+ }
+
+ oom: setMsg_P(EM_OOM, PG_RSTAT_HAVE_ERROR);
+
+ read_error: if (!(result_status & PG_RSTAT_HAVE_ERROR)) {
+ printf("READERROR!\n");fflush(stdout);
+ setMsg_P(EM_READ, PG_RSTAT_HAVE_ERROR);
+ }
+ conn_status = CONNECTION_BAD;
+ return -1;
+}
+
+int PGconnection::PGexecuteFormat(const char *format, ...) {
+ int32_t msgLen;
+ va_list va;
+ va_start(va, format);
+ msgLen = writeFormattedQuery(0, format, va);
+ va_end(va);
+ if (msgLen < 0)
+ return -1;
+ va_start(va, format);
+ msgLen = writeFormattedQuery(msgLen, format, va);
+ va_end(va);
+ if (msgLen) {
+ return -1;
+ }
+ result_status = PG_RSTAT_COMMAND_SENT;
+ return 0;
+}
+
+int PGconnection::build_startup_packet(char *packet, const char *dbName, const char *dbCharset) {
+ int packet_len = 4;
+ if (packet) {
+ memcpy(packet, "\0\003\0\0", 4);
+ }
+#define ADD_STARTUP_OPTION(optname, optval) \
+ do { \
+ if (packet) \
+ strcpy(packet + packet_len, (char *)optname); \
+ packet_len += strlen((char *)optname) + 1; \
+ if (packet) \
+ strcpy(packet + packet_len, (char *)optval); \
+ packet_len += strlen((char *)optval) + 1; \
+ } while(0)
+
+#define ADD_STARTUP_OPTION_P(optname, optval) \
+ do { \
+ if (packet) \
+ strcpy(packet + packet_len, (char *)optname); \
+ packet_len += strlen((char *)optname) + 1; \
+ if (packet) \
+ strcpy(packet + packet_len, (char *)optval); \
+ packet_len += strlen((char *)optval) + 1; \
+ } while(0)
+
+ if (_user && _user[0])
+ ADD_STARTUP_OPTION("user", _user);
+ if (dbName && dbName[0])
+ ADD_STARTUP_OPTION("database", dbName);
+ if (dbCharset && dbCharset[0])
+ ADD_STARTUP_OPTION("client_encoding", dbCharset);
+ ADD_STARTUP_OPTION_P("application_name", "Scaladis");
+#undef ADD_STARTUP_OPTION
+ if (packet)
+ packet[packet_len] = '\0';
+ packet_len++;
+
+ return packet_len;
+}
+
+int PGconnection::pqPacketSend(char pack_type, const char *buf, int buf_len) {
+ char *start = _buffer;
+ int l = _bufSize - 4;
+// int n;
+ if (pack_type) {
+ *start++ = pack_type;
+ l--;
+ }
+ *start++ = ((buf_len + 4) >> 24) & 0xff;
+ *start++ = ((buf_len + 4) >> 16) & 0xff;
+ *start++ = ((buf_len + 4) >> 8) & 0xff;
+ *start++ = (buf_len + 4) & 0xff;
+
+ if (buf) {
+ if (buf_len <= l) {
+ memcpy(start, buf, buf_len);
+ start += buf_len;
+ buf_len = 0;
+ } else {
+ memcpy(start, buf, l);
+ start += l;
+ buf_len -= l;
+ buf += l;
+ }
+ }
+ int err = send(SockH, _buffer, start - _buffer, 0);
+ if (err < 0) {
+ ESP_LOGE(PGTAG, "Send Error occurred during sending: errno %d", errno);
+ return err;
+ }
+ if (buf && buf_len) {
+ err = send(SockH, (const char *) buf, (size_t) buf_len, 0);
+ if (err < 0) {
+ ESP_LOGE(PGTAG, "Send2 Error occurred during sending: errno %d", errno);
+ return err;
+
+ }
+ }
+
+ return 0;
+}
+
+int PGconnection::pqGetc(char *buf) {
+ int i=0;
+// for (i = 0; !client->available() && i < 10; i++) {
+ while (i<10) {
+ if (dataAvailable()>0) break;
+ else {
+ vTaskDelay(i++);
+ }
+ }
+ if (i==10) return -1;
+
+ int len = read(SockH, (void *) buf, 1);
+ _available-=len;
+ return -1+len;
+}
+
+int PGconnection::pqGetInt4(int32_t *result) {
+ uint32_t tmp4 = 0;
+ uint8_t tmp, i;
+ int rt=0;
+ for (i = 0; i < 4; i++) {
+ rt=pqGetc((char *) &tmp);
+ if (rt) return -1;
+ tmp4 = (tmp4 << 8) | tmp;
+ }
+ *result = tmp4;
+ return 0;
+}
+
+int PGconnection::pqGetInt2(int16_t *result) {
+ uint16_t tmp2 = 0;
+ uint8_t tmp, i;
+ for (i = 0; i < 2; i++) {
+ if (pqGetc((char *) &tmp)) return -1;
+ tmp2 = (tmp2 << 8) | tmp;
+ }
+ *result = tmp2;
+ return 0;
+}
+
+int PGconnection::pqGetnchar(char *s, int len) {
+ while (len-- > 0) {
+ if (pqGetc(s++)) return -1;
+ }
+ return 0;
+}
+
+int PGconnection::pqGets(char *s, int maxlen) {
+ int len;
+ char z;
+ for (len = 0; len < maxlen; len++) {
+ if (pqGetc(&z)) return -1;
+ if (s) *s++ = z;
+ if (!z) return len + 1;
+ }
+ return -(len + 1);
+}
+
+int PGconnection::pqSkipnchar(int len) {
+ char dummy;
+ int i;
+ for (i=0;i<len;i++) read(SockH,&dummy,1);
+ /*
+ while (len-- > 0) {
+ if (pqGetc(&dummy))
+ return -1;
+ }
+ */
+ return 0;
+}
+
+int PGconnection::pqGetRow(void) {
+ int i;
+ int bufpos = 0;
+ int32_t len;
+ int16_t cols;
+
+ _null = 0;
+ if (pqGetInt2(&cols)) return -1;
+ if (cols != _nfields) return -3;
+
+ for (i = 0; i < _nfields; i++) {
+ if (pqGetInt4(&len)) return -1;
+ if (len < 0) {
+ _null |= 1 << i;
+ continue;
+ }
+ if (bufpos + len + 1 > _bufSize) {
+ return -2;
+ }
+ if (pqGetnchar(_buffer + bufpos, len))
+ return -1;
+ bufpos += len;
+ _buffer[bufpos++] = 0;
+ }
+ return 0;
+}
+
+int PGconnection::pqGetRowDescriptions(void) {
+ int i;
+ int16_t format;
+ int rc;
+ int bufpos;
+ if (pqGetInt2(&_nfields))
+ return -1;
+ if (_nfields > PG_MAX_FIELDS)
+ return -2; // implementation limit
+ _formats = 0;
+ bufpos = 0;
+
+ for (i = 0; i < _nfields; i++) {
+ if (!(_flags & PG_FLAG_IGNORE_COLUMNS)) {
+ if (bufpos >= _bufSize - 1)
+ return -2;
+ rc = pqGets(_buffer + bufpos, _bufSize - bufpos);
+ if (rc < 0)
+ return -1;
+ bufpos += rc;
+ } else {
+ if (pqGets(NULL, 8192) < 0) {
+ return -1;
+ }
+ }
+ if (pqSkipnchar(16))
+ return -1;
+ if (pqGetInt2(&format))
+ return -1;
+ format = format ? 1 : 0;
+ _formats |= format << i;
+ }
+ if (_formats)
+ return -3;
+ return 0;
+}
+
+void PGconnection::setMsg(const char *s, int type) {
+ strcpy(_buffer, s);
+ result_status = (result_status & ~PG_RSTAT_HAVE_MASK) | type;
+}
+
+void PGconnection::setMsg_P(const char *s, int type) {
+ strcpy(_buffer, s);
+ result_status = (result_status & ~PG_RSTAT_HAVE_MASK) | type;
+}
+
+int PGconnection::pqGetNotice(int type) {
+ int bufpos = 0;
+ char id;
+ int rc;
+ for (;;) {
+ if (pqGetc(&id)) goto read_error;
+ if (!id)
+ break;
+ if (id == 'S' || id == 'M') {
+ if (bufpos && bufpos < _bufSize - 1)
+ _buffer[bufpos++] = ':';
+ rc = pqGets(_buffer + bufpos, _bufSize - bufpos);
+ if (rc < 0)
+ goto read_error;
+ bufpos += rc - 1;
+ } else {
+ rc = pqGets(NULL, 8192);
+ if (rc < 0) goto read_error;
+ }
+ }
+ _buffer[bufpos] = 0;
+ result_status = (result_status & ~PG_RSTAT_HAVE_MASK) | type;
+ return 0;
+
+ read_error: if (!bufpos)
+ setMsg_P(EM_READ, PG_RSTAT_HAVE_ERROR);
+ return -1;
+}
+
+int PGconnection::pqGetNotify(int32_t msgLen) {
+ int32_t pid;
+ int bufpos, i;
+ if (pqGetInt4(&pid))
+ return -1;
+ msgLen -= 4;
+ bufpos = sprintf(_buffer, "%d:", pid);
+ if (msgLen > _bufSize - (bufpos + 1)) {
+ if (pqGetnchar(_buffer + bufpos, _bufSize - (bufpos + 1)))
+ return -1;
+ msgLen -= _bufSize - (bufpos + 1);
+ if (pqSkipnchar(msgLen))
+ return -1;
+ _buffer[msgLen = _bufSize - 1] = 0;
+
+ } else {
+ if (pqGetnchar(_buffer + bufpos, msgLen))
+ return -1;
+ _buffer[bufpos + msgLen] = 0;
+ msgLen += bufpos;
+ }
+ for (i = 0; i < msgLen; i++)
+ if (!_buffer[i])
+ _buffer[i] = ':';
+ return 0;
+}
+
+int PGconnection::dataAvailable() {
+ int res=0;
+// if (_available) return _available;
+ ioctl(SockH,FIONREAD,&res);
+ return res;
+}
+
+int PGconnection::writeMsgPart(const char *s, int len, int fine) {
+ while (len > 0) {
+ int n = len;
+ if (n > _bufSize - bufPos)
+ n = _bufSize - bufPos;
+ memcpy(_buffer + bufPos, s, n);
+ bufPos += n;
+ s += n;
+ len -= n;
+ if (bufPos >= _bufSize) {
+// if (client->write((uint8_t *) Buffer, bufPos) != (size_t) bufPos) return -1;
+ int err = send(SockH, _buffer, bufPos, 0);
+ if (err < 0)
+ return -1;
+ bufPos = 0;
+ }
+ }
+ if (bufPos && fine) {
+// if (client->write((uint8_t *) Buffer, bufPos) != (size_t) bufPos) return -1;
+ int err = send(SockH, _buffer, bufPos, 0);
+ if (err < 0)
+ return -1;
+
+ bufPos = 0;
+ }
+
+ return 0;
+}
+
+int32_t PGconnection::writeFormattedQuery(int32_t length, const char *format, va_list va) {
+ int32_t msgLen = 0;
+ const char *percent;
+ int blen, rc;
+#define LBUFLEN 32
+ char buf[LBUFLEN], znak;
+ if (length) {
+ length += 4;
+ bufPos = 0;
+ _buffer[bufPos++] = 'Q';
+ _buffer[bufPos++] = (length >> 24) & 0xff;
+ _buffer[bufPos++] = (length >> 16) & 0xff;
+ _buffer[bufPos++] = (length >> 8) & 0xff;
+ _buffer[bufPos++] = (length) & 0xff;
+ }
+ for (;;) {
+ percent = strchr(format, '%');
+ if (!percent)
+ break;
+ znak = percent[1];
+ if (!length) {
+ msgLen += (percent - format);
+ } else {
+ rc = writeMsgPart(format, percent - format, false);
+ if (rc)
+ goto write_error;
+ }
+ format = percent + 2;
+ if (znak == 's' || znak == 'n') {
+ char *str = va_arg(va, char *);
+ blen = (znak == 's') ? PGescapeString(str, NULL) : PGescapeName(str, NULL);
+ if (!length) {
+ msgLen += blen;
+ } else {
+ if (bufPos + blen > _bufSize) {
+ rc = writeMsgPart(NULL, 0, true);
+ if (rc)
+ goto write_error;
+ }
+ }
+ if (znak == 's') {
+ PGescapeString(str, _buffer + bufPos);
+ } else {
+ PGescapeName(str, _buffer + bufPos);
+ }
+ bufPos += blen;
+ continue;
+ }
+ if (znak == 'l' || znak == 'd') {
+ if (znak == 'l') {
+ long n = va_arg(va, long);
+ blen = snprintf(buf, LBUFLEN, "'%ld'", n);
+ } else {
+ int n = va_arg(va, int);
+ blen = snprintf(buf, LBUFLEN, "'%d'", n);
+ }
+ if (length) {
+ rc = writeMsgPart(buf, blen, false);
+ if (rc)
+ goto write_error;
+ } else {
+ msgLen += blen;
+ }
+ }
+ setMsg_P(EM_FORMAT, PG_RSTAT_HAVE_ERROR);
+ return -1;
+ }
+ blen = strlen(format);
+ if (length) {
+ rc = writeMsgPart(format, blen, false);
+ if (!rc) {
+ rc = writeMsgPart("\0", 1, true);
+ }
+ if (rc)
+ goto write_error;
+ } else {
+ msgLen += blen + 1;
+ }
+ return msgLen;
+
+ write_error: setMsg_P(EM_WRITE, PG_RSTAT_HAVE_ERROR);
+ conn_status = CONNECTION_BAD;
+ return -1;
+}
diff --git a/software/michi_funcs/SimplePgSQL_h.old b/software/michi_funcs/SimplePgSQL_h.old
new file mode 100644
index 0000000..9637ee5
--- /dev/null
+++ b/software/michi_funcs/SimplePgSQL_h.old
@@ -0,0 +1,209 @@
+/*
+ * SimplePgSQL.h - Lightweight PostgreSQL connector for Arduino
+ * Copyright (C) Bohdan R. Rau 2016 <ethanak@polip.com>
+ *
+ * SimplePgSQL is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * SimplePgSQL is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with SimplePgSQL. If not, write to:
+ * The Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "esp_system.h"
+#include "esp_netif.h"
+#include "lwip/err.h"
+#include "lwip/sockets.h"
+
+#ifndef _SIMPLEPGSQL
+#define _SIMPLEPGSQL 1
+
+typedef enum {
+ CONNECTION_OK, CONNECTION_BAD, CONNECTION_NEEDED, /* setDbLogin() needed */
+ /* Internal states here */
+ CONNECTION_AWAITING_RESPONSE, /* Waiting for a response from the postmaster. */
+ CONNECTION_AUTH_OK /* Received authentication; waiting for backend startup. */
+} ConnStatusType;
+
+#define PG_BUFFER_SIZE 4096
+
+// maximum number of fields in backend response
+// must not exceed number of bits in _formats and _null
+#define PG_MAX_FIELDS 32
+
+// ignore notices and notifications
+#define PG_FLAG_IGNORE_NOTICES 1
+// do not store column names
+#define PG_FLAG_IGNORE_COLUMNS 2
+
+// ready for next query
+#define PG_RSTAT_READY 1 // command sent
+#define PG_RSTAT_COMMAND_SENT 2 // column names in buffer
+#define PG_RSTAT_HAVE_COLUMNS 4 // row values in buffer
+#define PG_RSTAT_HAVE_ROW 8 // summary (number of tuples/affected rows) received
+#define PG_RSTAT_HAVE_SUMMARY 16 // error message in buffer
+#define PG_RSTAT_HAVE_ERROR 32 // notice/notification in buffer
+#define PG_RSTAT_HAVE_NOTICE 64
+
+#define PG_RSTAT_HAVE_MASK (PG_RSTAT_HAVE_COLUMNS | \
+ PG_RSTAT_HAVE_ROW | \
+ PG_RSTAT_HAVE_SUMMARY | \
+ PG_RSTAT_HAVE_ERROR | \
+ PG_RSTAT_HAVE_NOTICE)
+
+#define PG_RSTAT_HAVE_MESSAGE (PG_RSTAT_HAVE_ERROR | PG_RSTAT_HAVE_NOTICE)
+
+class PGconnection {
+public:
+
+ PGconnection(const int flags, const unsigned char *Buffer, const int bufSize);
+ /*
+ * returns connection status.
+ * passwd may be null in case of 'trust' authorization.
+ * only 'trust', 'password' and 'md5' (if compiled in)
+ * authorization modes are implemented.
+ * ssl mode is not implemented.
+ * database name defaults to user name *
+ */
+ int PGsetDbLogin(const char *ServerIP, int ServerPort, const char *dbName, const char *dbUser, const char *dbPasswd, const char *charset);
+ /*
+ * performs authorization tasks if needed
+ * returns current connection status
+ * must be called periodically until OK, BAD or NEEDED
+ */
+ int PGstatus(void);
+ /*
+ * sends termination command if possible
+ * closes client connection and frees internal buffer
+ */
+ void PGclose(void);
+ /*
+ * sends query to backend
+ * returns negative value on error
+ * or zero on success
+ */
+ int PGexecute(const char *query);
+
+ /* should be called periodically in idle state
+ * if notifications are enabled
+ * returns:
+ * - negative value on error
+ * - zero if no interesting data arrived
+ * - current data status if some data arrived
+ */
+ int PGgetData(void);
+ /*
+ * returns pointer to n-th column name in internal buffer
+ * if available or null if column number out of range
+ * will be invalidated on next getData call
+ */
+ char *PGgetColumn(int n);
+ /*
+ * returns pointer to n-th column value in internal buffer
+ * if available or null if column number out of range
+ * or value is NULL
+ * will be invalidated on next getData call
+ */
+ char *PGgetValue(int);
+ /*
+ * returns pointer to message (error or notice)
+ * if available or NULL
+ * will be invalidated on next getData call
+ */
+ char *PGgetMessage(void);
+ int PGdataStatus(void) {
+ return result_status;
+ }
+ ;
+ int PGnfields(void) {
+ return _nfields;
+ }
+ ;
+ int PGntuples(void) {
+ return _ntuples;
+ }
+ ;
+ /*
+ * returns length of escaped string
+ * single quotes and E prefix (if needed)
+ * will be added.
+ */
+ int PGescapeString(const char *inbuf, char *outbuf);
+ /*
+ * returns length of escaped string
+ * double quotes will be added.
+ */
+ int PGescapeName(const char *inbuf, char *outbuf);
+ /*
+ * sends formatted query to backend
+ * returns negative value on error
+ * or zero on success
+ * Formatting sequences:
+ * %s - string literal (will be escaped with escapeString)
+ * %n - name (will be escaped with escapeName)
+ * %d - int (single quotes will be added)
+ * %l - long int (single quotes will be added)
+ * %% - % character
+ */
+ int PGexecuteFormat(const char *format, ...);
+
+private:
+ int pqPacketSend(char pack_type, const char *buf, int buf_len);
+ int pqGetc(char *);
+ int pqGetInt4(int32_t *result);
+ int pqGetInt2(int16_t *result);
+ int pqGetnchar(char *s, int len);
+ int pqSkipnchar(int len);
+ int pqGets(char *s, int maxlen);
+ int pqGetRowDescriptions(void);
+ int pqGetRow(void);
+ void setMsg(const char *, int);
+ void setMsg_P(const char *, int);
+ int pqGetNotice(int);
+ int pqGetNotify(int32_t);
+ char *_user;
+ char *_passwd;
+// char *Buffer;
+ char *_buffer;
+// int bufSize;
+ int _bufSize;
+ int bufPos;
+ int writeMsgPart(const char *s, int len, int fine);
+ int32_t writeFormattedQuery(int32_t length, const char *format, va_list va);
+ int dataAvailable(void);
+ int build_startup_packet(char *packet, const char *db, const char *charset);
+ uint8_t conn_status;
+ uint8_t attempts;
+ /*
+ int32_t be_pid;
+ int32_t be_key;
+ */
+ int16_t _nfields;
+ int16_t _ntuples;
+ uint32_t _formats;
+ uint32_t _null;
+ uint8_t _binary;
+ uint8_t _flags;
+ uint32_t _available;
+ int result_status;
+ // network stuff
+ struct sockaddr_in DestAddr;
+ int SockH = -1;
+ int ipProtocol = 0;
+ int AddrFamily = 0;
+ int NetConnected=0;
+
+};
+
+#endif
diff --git a/software/michi_funcs/board_rev_A.cpp b/software/michi_funcs/board_rev_A.cpp
new file mode 100644
index 0000000..f71e27a
--- /dev/null
+++ b/software/michi_funcs/board_rev_A.cpp
@@ -0,0 +1,38 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "freertos/queue.h"
+#include "driver/gpio.h"
+#include "schrank_ant_pcb.h" //contains setting of board revision
+
+#ifndef ESP32_BOARD_REV_B
+
+void io_init(void)
+{
+
+#define GPO_BIT_MASK (1ULL << PHY_PWR)
+ gpio_config_t o_conf;
+ o_conf.intr_type = GPIO_INTR_DISABLE;
+ o_conf.mode = GPIO_MODE_OUTPUT;
+ o_conf.pin_bit_mask = GPO_BIT_MASK;
+ o_conf.pull_down_en = GPIO_PULLDOWN_ENABLE;
+ o_conf.pull_up_en = GPIO_PULLUP_DISABLE;
+ gpio_config(&o_conf);
+ gpio_set_level((gpio_num_t) PHY_PWR, 1);
+
+ // inputs
+ /*
+ #define GPI_BIT_MASK ((1ULL << SWITCH)|(1ULL << SWITCH))
+ gpio_config_t i_conf;
+ i_conf.intr_type = GPIO_INTR_DISABLE;
+ i_conf.mode = GPIO_MODE_INPUT;
+ i_conf.pin_bit_mask = GPI_BIT_MASK;
+ i_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
+ i_conf.pull_up_en = GPIO_PULLUP_DISABLE;
+ gpio_config(&i_conf);
+ */
+}
+
+#endif
diff --git a/software/michi_funcs/board_rev_A.hpp b/software/michi_funcs/board_rev_A.hpp
new file mode 100644
index 0000000..d5eff54
--- /dev/null
+++ b/software/michi_funcs/board_rev_A.hpp
@@ -0,0 +1,49 @@
+#ifndef _BOARD_REV_A_H_
+#define _BOARD_REV_A_H_
+
+#include "schrank_ant_pcb.h" //contains setting of board revision
+
+//board definitions for Rev A board (LAN8720 module)
+
+// +3,3V // Pin 1
+// GND // Pin 2
+// ESP_EN // Pin 3
+#define UHF_RXD_MISO 36 // pin 4 UART
+#define LCD_RXD_MISO 39 // pin 5 UART
+// NC pin 6
+#define I2Cint 35 // pin 7 EXP
+//#define SCL 33 // pin 8 EXP disabled Michi, all boards have SCL on 32
+//#define SDA 32 // pin 9 EXP disabled Michi, all boards have SDA on 33
+#define SCL 32 //new Michi 01.05.2022
+#define SDA 33 //new Michi 01.05.2022
+#define EMAC_RXD0_RMII 25 // pin 10 ETH
+#define EMAC_RXD1_RMII 26 // pin 11 ETH
+#define EMAC_RX_CRS_DV 27 // pin 12 ETH
+#define HS2_CLK 14 // pin 13 ETH
+#define PHY_PWR 12 // pin 14 ETH
+// // pin 15 GND
+#define I2C_SDA_40p 13 // pin 16 40p
+// NC pin 17 bis 22
+#define HS2_CMD 15 // pin 23 ETH
+#define HS2_DATA0 02 // pin 24 ETH
+#define ETH_CLKREF 0 // pin 25 ETH
+#define UHF_TXD_MOSI 04 // pin 26 UART
+#define LCD_TXD_MOSI 16 // pin 27 UART
+#define EMAC_CLK_OUT_180 17 // pin 28 ETH
+#define SPI_CS 05 // pin 29 40p
+#define MDIO_RMII 18 // pin 30 ETH
+#define EMAC_TXD0_RMII 19 // pin 31 ETH
+//NC pin 32
+#define EMAC_TX_EN_RMII 21 // pin 33 ETH
+#define RDR_RXD_MISO 03 // pin 34 UART
+#define RDR_TXD_MOSI 01 // pin 35 UART
+#define EMAC_TXD1_RMII 22 // pin 36 ETH
+#define MDC_RMII 23 // pin 37 ETH
+//GND pin 38, 39
+#define I2C_PORTEXP 0x22
+
+
+void io_init(void);
+
+#endif
+
diff --git a/software/michi_funcs/board_rev_B.cpp b/software/michi_funcs/board_rev_B.cpp
new file mode 100644
index 0000000..902340e
--- /dev/null
+++ b/software/michi_funcs/board_rev_B.cpp
@@ -0,0 +1,102 @@
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "freertos/queue.h"
+#include "driver/gpio.h"
+#include "driver/i2c.h"
+#include "schrank_ant_pcb.h" //contains setting of board revision
+
+#ifdef ESP32_BOARD_REV_B
+#include "board_rev_B.hpp"
+
+void io_init(void)
+{
+ #define GPO_BIT_MASK 0 //we dont have GPIO as outputs, all output is done via I2C Port Expander
+/* as we have not ouput, leave default settings
+ gpio_config_t o_conf;
+ o_conf.intr_type = GPIO_INTR_DISABLE;
+ o_conf.mode = GPIO_MODE_OUTPUT;
+ o_conf.pin_bit_mask = GPO_BIT_MASK;
+ o_conf.pull_down_en = GPIO_PULLDOWN_ENABLE;
+ o_conf.pull_up_en = GPIO_PULLUP_DISABLE;
+ gpio_config(&o_conf);
+*/
+ //gpio_set_level((gpio_num_t) PHY_PWR, 1);
+
+ // inputs
+
+ #define IRQ2Esp 34
+ #define I2Cext_INT 35
+ #define GPI_BIT_MASK ((1ULL<<IRQ2Esp) | (1ULL<<I2Cext_INT))
+ //io_conf.pin_bit_mask = (1ULL<<15);//bit mask of the pins that you want to set,e.g.GPIO15
+ gpio_config_t i_conf;
+ i_conf.intr_type = GPIO_INTR_DISABLE;
+ i_conf.mode = GPIO_MODE_INPUT;
+ i_conf.pin_bit_mask = GPI_BIT_MASK;
+ i_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
+ i_conf.pull_up_en = GPIO_PULLUP_DISABLE;
+ gpio_config(&i_conf);
+
+}
+
+
+
+
+esp_err_t i2c_init(void) {
+ int res1,res2;
+ int I2C_master_port = I2C_MASTER_NUM;
+ uint8_t I2C_buf[5];
+ i2c_config_t I2C_conf = { .mode = I2C_MODE_MASTER, .sda_io_num = I2C_MASTER_SDA_IO, .scl_io_num = I2C_MASTER_SCL_IO, .sda_pullup_en = GPIO_PULLUP_ENABLE, .scl_pullup_en = GPIO_PULLUP_ENABLE, .master { .clk_speed = I2C_MASTER_FREQ_HZ, } };
+ i2c_param_config(I2C_master_port, &I2C_conf);
+
+ printf("\nInstall i2c_driver: Mode=%d sda_io_num=%d scl_io_num=%d\n", I2C_MODE_MASTER, I2C_MASTER_SDA_IO, I2C_MASTER_SCL_IO);
+ res1=i2c_driver_install(I2C_master_port, I2C_conf.mode,
+ I2C_MASTER_RX_BUF_DISABLE, I2C_MASTER_TX_BUF_DISABLE, 0);
+ ESP_ERROR_CHECK_WITHOUT_ABORT(res1);
+ printf("After i2c_driver_install\n");
+ vTaskDelay(500 / portTICK_PERIOD_MS);
+
+//1. Init AW9523 on address 0x00
+ I2C_buf[0] = 0x7F; // write to AW9523 Soft Reset register
+ I2C_buf[1] = 0x00; // write data to output port 1
+ I2C_buf[2] = 0x00; // write data to output port 1
+ printf("I2C write to bus %d to I2C-Adress %d byte0 = %d byte 1 = %d\n", I2C_MASTER_NUM, I2C_PORTEXP1, I2C_buf[0], I2C_buf[1] );
+ res1 = i2c_master_write_to_device(I2C_MASTER_NUM, I2C_PORTEXP1, I2C_buf, 3, I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS);
+ printf("I2C write to bus %d to I2C-Adress %d byte0 = %d byte 1 = %d\n", I2C_MASTER_NUM, I2C_PORTEXP2, I2C_buf[0], I2C_buf[1] );
+ res2 = i2c_master_write_to_device(I2C_MASTER_NUM, I2C_PORTEXP2, I2C_buf, 3, I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS);
+ printf("\nMaster wrote %d %d %02X\n", res1,res2,I2C_buf[0]);
+ printf("After AW9523 soft-RESET\n");
+ vTaskDelay(500 / portTICK_PERIOD_MS);
+
+//2. Set outputs push pull in AW9523 Global Control Register
+ I2C_buf[0] = 0x11; // write data to Global Control Register
+ I2C_buf[1] = 0x10; // write data to Global Control Register, set Bit4 to Push Pull
+ res1 = i2c_master_write_to_device(I2C_MASTER_NUM, I2C_PORTEXP1, I2C_buf, 2, I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS);
+ res2 = i2c_master_write_to_device(I2C_MASTER_NUM, I2C_PORTEXP2, I2C_buf, 2, I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS);
+ printf("\nMaster wrote %d %d %02X\n", res1,res2,I2C_buf[0]);
+
+ I2C_buf[0] = 0x02; // write data to output port 1
+ I2C_buf[1] = 0xFF; // write data to output port 1
+ I2C_buf[2] = 0xFF; // write data to output port 1
+ I2C_buf[3] = 0x00; // write data to output port 1
+ I2C_buf[4] = 0x00; // write data to output port 1
+ res1 = i2c_master_write_to_device(I2C_MASTER_NUM, I2C_PORTEXP1, I2C_buf, 5, I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS);
+ res2 = i2c_master_write_to_device(I2C_MASTER_NUM, I2C_PORTEXP2, I2C_buf, 5, I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS);
+ printf("\nMaster wrote %d %d %02X\n", res1,res2,I2C_buf[0]);
+ printf("After AW9523 all GPIOs HIGH\n");
+ vTaskDelay(500 / portTICK_PERIOD_MS);
+
+
+
+//2. Set AW9523-PhyPower to high
+/* I2C_buf[0] = 0x02; // write to register 0x02
+ I2C_buf[1] = 255; // set all pins high
+ I2C_buf[2] = 255; // set all pins high
+ res1 = i2c_master_write_to_device(I2C_MASTER_NUM, I2C_PORTEXP2, I2C_buf, 3, I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS);
+*/
+ return res1;
+}
+
+#endif
diff --git a/software/michi_funcs/board_rev_B.hpp b/software/michi_funcs/board_rev_B.hpp
new file mode 100644
index 0000000..74d4404
--- /dev/null
+++ b/software/michi_funcs/board_rev_B.hpp
@@ -0,0 +1,63 @@
+#ifndef _BOARD_REV_B_H_
+#define _BOARD_REV_B_H_
+
+//board definitions for Rev B board (LAN8720 on board)
+#include "schrank_ant_pcb.h" //contains setting of board revision
+
+
+#define UHF_RXD_MISO 36 // pin 4 UART
+#define LCD_RXD_MISO 39 // pin 5 UART
+
+#define SCL 32 //new Michi 01.05.2022
+#define SDA 33 //new Michi 01.05.2022
+
+
+#define I2C_MASTER_SCL_IO SCL
+#define I2C_MASTER_SDA_IO SDA
+#define I2C_MASTER_NUM 0 /*!< I2C master i2c port number, the number of i2c peripheral interfaces available will depend on the chip */
+#define I2C_MASTER_FREQ_HZ 100000 /*!< I2C master clock frequency */
+#define I2C_MASTER_TX_BUF_DISABLE 0 /*!< I2C master doesn't need buffer */
+#define I2C_MASTER_RX_BUF_DISABLE 0 /*!< I2C master doesn't need buffer */
+#define I2C_MASTER_TIMEOUT_MS 1000
+#define I2C_PORTEXP 0x22
+#define I2Cint 35
+#define EMAC_RXD0_RMII 25 // pin 10 ETH
+#define EMAC_RXD1_RMII 26 // pin 11 ETH
+#define EMAC_RX_CRS_DV 27
+#define HS2_CLK 14
+//#define PHY_PWR is in Rev B handled by I2C port expander, no longer a GPIO
+//#define I2C_SDA_40p 13 // the I2C to the RasPi 40 port header is switched with the second port expander
+#define HS2_CMD 15
+#define HS2_DATA0 02
+#define ETH_CLKREF 0
+#define UHF_TXD_MOSI 04
+#define LCD_TXD_MOSI 5
+//#define EMAC_CLK_OUT_180 17 // there is no GPIO17 on the WROVER module
+#define SPI_CS 13// was: 05
+#define MDIO_RMII 18
+#define EMAC_TXD0_RMII 19
+#define EMAC_TX_EN_RMII 21
+#define RDR_RXD_MISO 03
+#define RDR_TXD_MOSI 01
+#define EMAC_TXD1_RMII 22
+#define MDC_RMII 23
+//#define I2C_PORTEXP 0x22
+
+#define I2C_MASTER_NUM 0 /*!< I2C master i2c port number, the number of i2c peripheral interfaces available will depend on the chip */
+
+// AW9523 I2C adress P40 1011000 = 88, 0x58
+// AW9523 I2C adress 10110 AD1 AD0 RW
+//#define AW9523B_I2C_ADDRESS_MAIN 88 //0x58
+#define AW9523B_I2C_ADDRESS_MAIN 0x58
+
+// AW9523 I2C adress P40 10110100 = 90, 0x5A
+#define AW9523B_I2C_ADDRESS_ETH 0x5A //90dec
+#define I2C_PORTEXP1 0x58
+#define I2C_PORTEXP2 0x5A
+
+void io_init(void);
+esp_err_t i2c_init(void);
+void kolban_i2cscanner(void );
+
+#endif
+
diff --git a/software/michi_funcs/component.mk b/software/michi_funcs/component.mk
new file mode 100644
index 0000000..a98f634
--- /dev/null
+++ b/software/michi_funcs/component.mk
@@ -0,0 +1,4 @@
+#
+# "main" pseudo-component makefile.
+#
+# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
diff --git a/software/michi_funcs/db_funcs.cpp b/software/michi_funcs/db_funcs.cpp
new file mode 100644
index 0000000..c1a2f56
--- /dev/null
+++ b/software/michi_funcs/db_funcs.cpp
@@ -0,0 +1,404 @@
+/*
+How to use db_funcs:
+1. in main.cpp #include "db_funcs.h"
+2. in main.cpp, in init()
+ while (network_connected != 1) { vTaskDelay(2000 / portTICK_PERIOD_MS); }
+ strcpy(dbnam ,(char *)"c10mm");
+ strcpy(pg_server_ip ,(char *)"192.168.178.100");
+
+2.a pqConnection = new TpgConnection(); // Verbindungsobject erzeugen
+2.b if ( pqConnection->db_connect( pg_server_ip, srvpo, dbnam, PGUser, PGPassword, PGCharset) == 0) //verbinden
+2.c query1 = new TResultSet( pqConnection->PGconn );
+2.d query1->db_query("select type, state from items limit 10;");
+2.4 for (int iRow = 0; iRow < query1->NumRows-1; iRow++)
+ {
+ std::cout << query1->Zeile[iRow].Spalte[0] << " | ";
+ std::cout << query1->Zeile[iRow].Spalte[1] << " | ";
+ printf("\n");
+ }
+
+3. query1->db_query("select bla bla;"); //naechste Abfrage
+ //oder neues Abfrage-Object erstellen: query2 = new TResultSet( pqConnection->PGconn );
+
+4. in main.cpp, main_loop()
+ db_loop(); //db_loop regelmaessig aufrufen, damit NOTIFY Nachrichten verarbeitet werden.
+
+
+5. Auf Notification reagieren: Vor der mail() Schleife die NOTIFIY Nachrichten registrieren
+ db_query("listen door_open"); //register listen handler for NOTIFY door_open <door_no>
+ db_query("listen door_close"); //register listen handler for NOTIFY door_close <door_no>
+
+*/
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include "esp_netif.h"
+#include "esp_eth.h"
+#include "esp_log.h"
+#include "SimplePgSQL.h"
+
+//For ResultSet:
+#include "db_funcs.h"
+#include <vector>
+#include <iostream>
+#include <string>
+
+static int pg_flags = 0;
+
+// PGSQL
+#define PGBufferSize 16384
+#define PGCharset "utf-8"
+#define PGUser "micha"
+#define PGPassword "micha"
+const TickType_t delay1ms = 1 / portTICK_PERIOD_MS;
+const TickType_t delay10ms = 10 / portTICK_PERIOD_MS;
+const TickType_t delay100ms = 100 / portTICK_PERIOD_MS;
+
+//static PGconnection *PGconn = NULL;
+static unsigned char PGbuffer[PGBufferSize];
+static char lastNotify[40];
+
+static const char *PGTAG = "SCALADIS-PG";
+
+static unsigned char pg_lastError_string[80];
+
+//TResultSet ResultSetObject(1);
+
+TpgConnection::TpgConnection()
+{
+ PGconn = NULL;
+}
+
+
+int TpgConnection::db_connect(const char *pg_server_ip, int pg_port, const char *s_db_name, const char *s_db_user, const char *s_db_passwd, const char *s_db_charset)
+{
+//CONNECTION_OK, CONNECTION_BAD, CONNECTION_NEEDED, /* setDbLogin() needed */
+//CONNECTION_AWAITING_RESPONSE, /* Waiting for a response from the postmaster. */
+// CONNECTION_AUTH_OK
+ int loop_fuse = 0;
+ ESP_LOGI(PGTAG,"db_connect Enter");
+ PGconn = new PGconnection(pg_flags, PGbuffer, PGBufferSize);
+ if (PGconn == NULL) { ESP_LOGE(PGTAG, "error creating PGconn object"); }
+
+ last_pgError = PGconn->setDbLogin(pg_server_ip, pg_port, s_db_name, s_db_user, s_db_passwd, s_db_charset);
+
+ while ( (last_pgError != CONNECTION_OK) || (last_pgError != CONNECTION_BAD)
+ || (last_pgError != CONNECTION_NEEDED) )
+ {
+ last_pgError = PGconn->status();
+ vTaskDelay(delay10ms);
+ loop_fuse++;
+ if (loop_fuse > 100) { ESP_LOGE(PGTAG, "PGsetDbLogin break after 10 attempts. "); break; }
+ }
+
+ //ResultSetObject.Zeile.resize(1);
+
+ switch (last_pgError) {
+ case CONNECTION_NEEDED: { ESP_LOGE(PGTAG,"connection needed: %s", PGconn->getMessage()); return -1; }
+ case CONNECTION_OK: { ESP_LOGI(PGTAG,"db_connect sucessful (OK) to db %s", s_db_name); return 0; }
+ case CONNECTION_BAD: { ESP_LOGE(PGTAG,"ERROR: %s", PGconn->getMessage()); return -1; }
+ case CONNECTION_AWAITING_RESPONSE: { ESP_LOGI(PGTAG,"ERROR AWAITING_RESPONSE: %s", PGconn->getMessage()); return -1; }
+ case CONNECTION_AUTH_OK: { ESP_LOGI(PGTAG,"db_connect sucessful (AUTH_OK) to db %s", s_db_name); return 0; }
+ }
+
+
+ ESP_LOGI(PGTAG,"ResultSet created");
+
+ ESP_LOGI(PGTAG,"db_connect EXIT. last_pgError=%d", last_pgError);
+ return 0;
+}
+
+int TpgConnection::db_disconnect(void)
+{
+ ESP_LOGI(PGTAG,"db_disconnect ENTER");
+ if (PGconn == NULL) { ESP_LOGE(PGTAG, "error destroy PGconn object. PGconn = NULL"); return -1; }
+ PGconn->close();
+ delete PGconn;
+ PGconn = NULL;
+ ESP_LOGI(PGTAG,"db_disconnect EXIT");
+ return 0;
+}
+
+void TpgConnection::db_loop(void)
+{
+// bool readyToQuery = false;
+ char *msg = NULL;
+ last_pgError = PGconn->status();
+ if (last_pgError == CONNECTION_BAD || last_pgError == CONNECTION_NEEDED)
+ {
+ ESP_LOGE(PGTAG, "ERROR: %s", PGconn->getMessage() );
+ } else
+ {
+ last_pgError = PGconn->getData();
+ if (last_pgError < 0)
+ {
+ ESP_LOGE(PGTAG, "ERROR: %s", PGconn->getMessage() );
+ } else
+ {
+ if (last_pgError > 0)
+ {
+ //some data arrive
+ msg = PGconn->getMessage();
+ if (msg != NULL)
+ {
+ sprintf( lastNotify, msg); //copy message, to save the NOTIFY
+ ESP_LOGI(PGTAG,"NOTIFY %s", lastNotify);
+ }
+ }
+ }
+ }
+}
+
+// ********************************************************************************************
+
+TResSetRows::TResSetRows()
+{
+ Spalte.resize(1);
+ //ESP_LOGE(PGTAG, "--------------------------> TResSetRows constructor called");
+}
+
+
+TResSetRows::~TResSetRows()
+{
+ Spalte.resize(0);
+ //ESP_LOGE(PGTAG, "--------------------------> TResSetRows destructor called");
+}
+
+TResultSet::TResultSet(PGconnection *PGconnFromCon)
+{
+ PGconn = PGconnFromCon;
+ if (PGconnFromCon == NULL)
+ {
+ ESP_LOGE(PGTAG, "ERROR");
+ ESP_LOGE(PGTAG, "TResultSet::TResultSet(PGconnection *PGconnFromCon) -> PGconnFromCon ist NULL!!!!!!");
+ ESP_LOGE(PGTAG, "");
+ }
+ Zeile.resize(1);
+ NumRows = 0;
+ //ESP_LOGE(PGTAG, "--------------------------> TResultSet constructor called");
+}
+
+TResultSet::~TResultSet()
+{
+ //Speicher aufraeumen
+ ESP_LOGI(PGTAG,"\nTResultSet Destructor called"); //fflush (stdout);
+}
+
+int TResultSet::db_query(const char *sql)
+{
+ int last_pgError;
+ bool queryEnded;
+
+// ESP_LOGI(PGTAG,"db_query ENTER: %s", sql); fflush (stdout);
+ NumRows = 0;//reset NumRows.
+
+ //sprintf(lbuf, "SELECT name,gname FROM accounts WHERE id=(cast(x'%s' AS int));", info);
+ last_pgError = PGconn->execute(sql);
+ if (last_pgError < 0)
+ {
+ printf("ERROR: TResultSet::db_query: PGexecute: %s", PGconn->getMessage());
+ return last_pgError;
+ } else
+ {
+ vTaskDelay(1);
+ queryEnded = false;
+ while (queryEnded == false)
+ {
+ last_pgError = PGconn->getData();
+ if (last_pgError & PG_RSTAT_HAVE_COLUMNS) { process_db_col(); }
+ if (last_pgError & PG_RSTAT_HAVE_ROW) { process_db_row(); }
+ if (last_pgError & PG_RSTAT_HAVE_SUMMARY) { process_db_summary(); }
+ if (last_pgError & PG_RSTAT_HAVE_MESSAGE) { process_db_haveMsg(); }
+ if (last_pgError & PG_RSTAT_READY) { process_db_ready(); }
+ if (last_pgError & PG_RSTAT_COMMAND_SENT) { process_db_cmd_send(); }
+ if (last_pgError & PG_RSTAT_HAVE_ERROR) { process_db_have_error();
+ printf("\n Error from Postgres: ");
+ if ( PGconn->getMessage() != NULL ) { printf("---> %s\n", PGconn->getMessage() ); }
+ fflush (stdout);
+ queryEnded = true; }
+ if (last_pgError & PG_RSTAT_HAVE_NOTICE) { process_db_have_notice(); }
+
+ if (last_pgError < 0)
+ {
+ queryEnded = true;
+ printf("Get Data Error: %d\n", last_pgError );
+ if ( PGconn->getMessage() != NULL ) { printf("---> %s\n", PGconn->getMessage() ); }
+ fflush (stdout);
+ }
+ if (last_pgError == CONNECTION_BAD || last_pgError == CONNECTION_NEEDED)
+ {
+ queryEnded = true;
+ //seem to be the normal end. No Error Message.
+ //ESP_LOGE(PGTAG, "ERROR: CONNECTION BAD or NEEDED:" );
+ //if ( PGconn->getMessage() != NULL ) { ESP_LOGE(PGTAG, "----> %s", PGconn->getMessage() ); }
+ }
+ if (last_pgError == 0) { queryEnded = true; //no more data }
+ }
+
+
+ } //while (queryEnded == false)
+ }
+// ESP_LOGI(PGTAG,"db_query EXIT"); fflush (stdout);
+ return 0;
+}
+
+int TResultSet::db_exec(const char *sql)
+{
+ int last_pgError;
+ bool queryEnded;
+
+// ESP_LOGI(PGTAG,"db_query ENTER: %s", sql); fflush (stdout);
+ NumRows = 0;//reset NumRows.
+
+ last_pgError = PGconn->execute(sql);//call old version from 17.02.2022
+ if (last_pgError < 0)
+ {
+ return last_pgError;
+ } else
+ {
+ vTaskDelay(1);
+ queryEnded = false;
+ while (queryEnded == false)
+ {
+ last_pgError = PGconn->getData();
+ if (last_pgError & PG_RSTAT_HAVE_COLUMNS) { process_db_col(); }
+ if (last_pgError & PG_RSTAT_HAVE_ROW) { process_db_row(); }
+ if (last_pgError & PG_RSTAT_HAVE_SUMMARY) { process_db_summary(); }
+ if (last_pgError & PG_RSTAT_HAVE_MESSAGE) { process_db_haveMsg(); }
+ if (last_pgError & PG_RSTAT_READY) { process_db_ready(); }
+ if (last_pgError & PG_RSTAT_COMMAND_SENT) { process_db_cmd_send(); }
+ if (last_pgError & PG_RSTAT_HAVE_ERROR) {
+ process_db_have_error();
+ printf("\n Error from Postgres: ");
+ if ( PGconn->getMessage() != NULL ) { printf("---> %s\n", PGconn->getMessage() ); }
+ fflush (stdout);
+ queryEnded = true; }
+ if (last_pgError & PG_RSTAT_HAVE_NOTICE) { process_db_have_notice(); }
+
+ if (last_pgError < 0)
+ {
+ queryEnded = true;
+ printf("Insert/Update Error: %d\n", last_pgError );
+ if ( PGconn->getMessage() != NULL ) { printf("---> %s\n", PGconn->getMessage() ); }
+ fflush (stdout);
+ }
+ if (last_pgError == CONNECTION_BAD || last_pgError == CONNECTION_NEEDED)
+ {
+ queryEnded = true;
+ //seem to be the normal end. No Error Message.
+ //ESP_LOGE(PGTAG, "ERROR: CONNECTION BAD or NEEDED:" );
+ //if ( PGconn->getMessage() != NULL ) { ESP_LOGE(PGTAG, "----> %s", PGconn->getMessage() ); }
+ }
+ if (last_pgError == 0) { queryEnded = true; //no more data }
+ }
+
+
+ } //while (queryEnded == false)
+ }
+ fflush (stdout);
+ return 0;
+}
+
+
+void TResultSet::process_db_col(void)
+{
+ int nCols;
+ int iCurrCol;
+// ESP_LOGI(PGTAG,"---> PG_RSTAT_HAVE_COLS");
+ printf("Spalten gefunden: \n" );
+ fflush (stdout);
+
+ //iRow = 1; // erste Zeile hat die Spaltennamen!
+ CurrentRow = 1;
+ nCols = PGconn->nfields();
+ this->NumCols = nCols;
+ Zeile[0].numCols = nCols;
+
+ Zeile.resize( 1 ); //erste Zeile
+ Zeile[0].Spalte.resize(nCols);
+// printf("Set Result buffer auf Spalten: %d\n", nCols );
+ fflush (stdout);
+ //alloc 1 row only, for the column names. More is added later.
+ for (iCurrCol = 0; iCurrCol < nCols; iCurrCol++)
+ {
+ if (iCurrCol) printf(" | ");
+ this->Zeile[0].Spalte[iCurrCol] = PGconn->getColumn(iCurrCol);
+// std::cout << Zeile[0].Spalte[iCurrCol] ;
+// fflush (stdout);
+ }
+
+ printf("\n==========\n");
+ fflush(stdout);
+}
+
+void TResultSet::process_db_row(void)
+{
+ //char *msg;
+ int iRow;
+ int iCurrField;
+ std::string sFeld;
+// ESP_LOGI(PGTAG,"---> PG_RSTAT_HAVE_ROW");
+ this->CurrentRow++;
+ NumRows++;
+ iRow = this->CurrentRow;
+ this->Zeile.resize( iRow ); //naechste Zeile
+ this->Zeile[iRow-1].Spalte.resize( this->Zeile[0].numCols );
+
+// printf("Row %d:", iRow );
+ for (iCurrField = 0; iCurrField < PGconn->nfields(); iCurrField++)
+ {
+ //printf(" | ");
+ //msg = PGconn->getValue( iCurrField );
+ //copy the string, to save the result
+ if (!PGconn->getValue( iCurrField ) )
+ { Zeile[iRow-1].Spalte[iCurrField].copy( (char *)"NULL", 0, 4); }
+ else
+ {
+ sFeld = PGconn->getValue( iCurrField );
+ Zeile[iRow-1].Spalte[iCurrField] = sFeld; //assign string to string
+ }
+ //printf(" %s", msg);fflush(stdout);
+// std::cout << Zeile[iRow-1].Spalte[iCurrField] << " | " ;
+ }
+// std::cout << "\n ";
+}
+
+
+
+
+
+void TResultSet::process_db_summary(void)
+{
+// ESP_LOGI(PGTAG,"---> PG_RSTAT_HAVE_SUMMARY");
+}
+
+void TResultSet::process_db_haveMsg(void)
+{
+// ESP_LOGI(PGTAG,"---> PG_RSTAT_HAVE_MESSAGE");
+}
+
+void TResultSet::process_db_ready(void)
+{
+// ESP_LOGI(PGTAG,"---> PG_RSTAT_READY");
+}
+
+void TResultSet::process_db_cmd_send(void)
+{
+// ESP_LOGI(PGTAG,"---> PG_RSTAT_COMMAND_SENT");
+}
+
+void TResultSet::process_db_have_error(void)
+{
+// ESP_LOGE(PGTAG,"---> PG_RSTAT_HAVE_ERROR");
+}
+
+void TResultSet::process_db_have_notice(void)
+{
+// ESP_LOGI(PGTAG,"---> PG_RSTAT_HAVE_NOTICE");
+}
+
+
+
+
diff --git a/software/michi_funcs/db_funcs.h b/software/michi_funcs/db_funcs.h
new file mode 100644
index 0000000..cf26c84
--- /dev/null
+++ b/software/michi_funcs/db_funcs.h
@@ -0,0 +1,66 @@
+/*
+ * db_funcs.h
+ *
+ * Created on: 11.01.2022
+ * Author: michi
+ */
+
+#ifndef DB_FUNCS_H_
+#define DB_FUNCS_H_
+//#include "schrank_ant_pcb.h"
+#include <vector>
+#include <iostream>
+#include <string>
+#include "SimplePgSQL.h"
+
+class TpgConnection { // rows
+ public:
+ TpgConnection();
+ ~TpgConnection();
+ int db_connect(const char *pg_server_ip, int pg_port, const char *s_db_name, const char *s_db_user, const char *s_db_passwd, const char *s_db_charset);
+ int db_disconnect(void);
+ void db_loop(void);
+ PGconnection *PGconn;
+ int last_pgError;
+ };
+
+
+//int db_query(const char *sql);
+
+class TResSetRows { // 1 entry is a collection of string fields
+ public:
+ TResSetRows(); //constructor
+ ~TResSetRows(); //destructor
+ std::vector<std::string> Spalte;
+ int numCols;
+ int CurrCol;
+
+ };
+
+class TResultSet { // rows
+ public:
+ TResultSet(PGconnection *PGconnFromCon);
+ ~TResultSet();
+ std::vector<TResSetRows> Zeile;
+ int CurrentRow;
+ int db_query(const char *sql);
+ int db_exec(const char *sql);
+ int NumRows;
+ int NumCols;
+
+ private:
+ PGconnection *PGconn;
+ void process_db_row(void);
+ void process_db_col(void);
+
+ void process_db_summary(void);
+ void process_db_haveMsg(void);
+ void process_db_ready(void);
+ void process_db_cmd_send(void);
+ void process_db_have_error(void);
+ void process_db_have_notice(void);
+ };
+
+
+
+#endif /* DB_FUNCS_H_ */
diff --git a/software/michi_funcs/sdcard_funcs.cpp b/software/michi_funcs/sdcard_funcs.cpp
new file mode 100644
index 0000000..93f8e20
--- /dev/null
+++ b/software/michi_funcs/sdcard_funcs.cpp
@@ -0,0 +1,153 @@
+/* SD card and FAT filesystem example.
+ This example code is in the Public Domain (or CC0 licensed, at your option.)
+
+ Unless required by applicable law or agreed to in writing, this
+ software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
+ CONDITIONS OF ANY KIND, either express or implied.
+*/
+
+// This example uses SDMMC peripheral to communicate with SD card.
+
+#include <string.h>
+#include <sys/unistd.h>
+#include <sys/stat.h>
+#include "esp_vfs_fat.h"
+#include "sdmmc_cmd.h"
+#include "driver/sdmmc_host.h"
+
+static const char *TAG = "example";
+
+#define MOUNT_POINT "/sdcard"
+
+
+void test_sdcard(void)
+{
+ esp_err_t ret;
+
+ // Options for mounting the filesystem.
+ // If format_if_mount_failed is set to true, SD card will be partitioned and
+ // formatted in case when mounting fails.
+ esp_vfs_fat_sdmmc_mount_config_t mount_config = {
+#ifdef CONFIG_EXAMPLE_FORMAT_IF_MOUNT_FAILED
+ .format_if_mount_failed = true,
+#else
+ .format_if_mount_failed = false,
+#endif // EXAMPLE_FORMAT_IF_MOUNT_FAILED
+ .max_files = 5,
+ .allocation_unit_size = 16 * 1024
+ };
+ sdmmc_card_t *card;
+ const char mount_point[] = MOUNT_POINT;
+ ESP_LOGI(TAG, "Initializing SD card");
+
+ // Use settings defined above to initialize SD card and mount FAT filesystem.
+ // Note: esp_vfs_fat_sdmmc/sdspi_mount is all-in-one convenience functions.
+ // Please check its source code and implement error recovery when developing
+ // production applications.
+
+ ESP_LOGI(TAG, "Using SDMMC peripheral");
+ sdmmc_host_t host = SDMMC_HOST_DEFAULT();
+
+ // This initializes the slot without card detect (CD) and write protect (WP) signals.
+ // Modify slot_config.gpio_cd and slot_config.gpio_wp if your board has these signals.
+ sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
+
+ // Set bus width to use:
+#ifdef CONFIG_EXAMPLE_SDMMC_BUS_WIDTH_4
+ slot_config.width = 4;
+#else
+ slot_config.width = 1;
+#endif
+
+ // On chips where the GPIOs used for SD card can be configured, set them in
+ // the slot_config structure:
+#ifdef CONFIG_SOC_SDMMC_USE_GPIO_MATRIX
+ slot_config.clk = CONFIG_EXAMPLE_PIN_CLK;
+ slot_config.cmd = CONFIG_EXAMPLE_PIN_CMD;
+ slot_config.d0 = CONFIG_EXAMPLE_PIN_D0;
+#ifdef CONFIG_EXAMPLE_SDMMC_BUS_WIDTH_4
+ slot_config.d1 = CONFIG_EXAMPLE_PIN_D1;
+ slot_config.d2 = CONFIG_EXAMPLE_PIN_D2;
+ slot_config.d3 = CONFIG_EXAMPLE_PIN_D3;
+#endif // CONFIG_EXAMPLE_SDMMC_BUS_WIDTH_4
+#endif // CONFIG_SOC_SDMMC_USE_GPIO_MATRIX
+
+ // Enable internal pullups on enabled pins. The internal pullups
+ // are insufficient however, please make sure 10k external pullups are
+ // connected on the bus. This is for debug / example purpose only.
+ slot_config.flags |= SDMMC_SLOT_FLAG_INTERNAL_PULLUP;
+
+ ESP_LOGI(TAG, "Mounting filesystem");
+ ret = esp_vfs_fat_sdmmc_mount(mount_point, &host, &slot_config, &mount_config, &card);
+
+ if (ret != ESP_OK) {
+ if (ret == ESP_FAIL) {
+ ESP_LOGE(TAG, "Failed to mount filesystem. "
+ "If you want the card to be formatted, set the EXAMPLE_FORMAT_IF_MOUNT_FAILED menuconfig option.");
+ } else {
+ ESP_LOGE(TAG, "Failed to initialize the card (%s). "
+ "Make sure SD card lines have pull-up resistors in place.", esp_err_to_name(ret));
+ }
+ return;
+ }
+ ESP_LOGI(TAG, "Filesystem mounted");
+
+ // Card has been initialized, print its properties
+ sdmmc_card_print_info(stdout, card);
+
+ // Use POSIX and C standard library functions to work with files:
+
+ // First create a file.
+ const char *file_hello = MOUNT_POINT"/hello.txt";
+
+ ESP_LOGI(TAG, "Opening file %s", file_hello);
+ FILE *f = fopen(file_hello, "w");
+ if (f == NULL) {
+ ESP_LOGE(TAG, "Failed to open file for writing");
+ return;
+ }
+ fprintf(f, "Hello %s!\n", card->cid.name);
+ fclose(f);
+ ESP_LOGI(TAG, "File written");
+
+ const char *file_foo = MOUNT_POINT"/foo.txt";
+
+ // Check if destination file exists before renaming
+ struct stat st;
+ if (stat(file_foo, &st) == 0) {
+ // Delete it if it exists
+ unlink(file_foo);
+ }
+
+ // Rename original file
+ ESP_LOGI(TAG, "Renaming file %s to %s", file_hello, file_foo);
+ if (rename(file_hello, file_foo) != 0) {
+ ESP_LOGE(TAG, "Rename failed");
+ return;
+ }
+
+ // Open renamed file for reading
+ ESP_LOGI(TAG, "Reading file %s", file_foo);
+ f = fopen(file_foo, "r");
+ if (f == NULL) {
+ ESP_LOGE(TAG, "Failed to open file for reading");
+ return;
+ }
+
+ // Read a line from file
+ char line[64];
+ fgets(line, sizeof(line), f);
+ fclose(f);
+
+ // Strip newline
+ char *pos = strchr(line, '\n');
+ if (pos) {
+ *pos = '\0';
+ }
+ ESP_LOGI(TAG, "Read from file: '%s'", line);
+
+ // All done, unmount partition and disable SDMMC peripheral
+ esp_vfs_fat_sdcard_unmount(mount_point, card);
+ ESP_LOGI(TAG, "Card unmounted");
+}
+
diff --git a/software/michi_funcs/sdcard_funcs.hpp b/software/michi_funcs/sdcard_funcs.hpp
new file mode 100644
index 0000000..495ac4a
--- /dev/null
+++ b/software/michi_funcs/sdcard_funcs.hpp
@@ -0,0 +1,6 @@
+#ifndef _SDCARD_FUNCS_H_
+#define _SDCARD_FUNCS_H_
+
+void test_sdcard(void);
+
+#endif