Чтение данных с датчика MIS-3600-006DI

Да , я в лаптях хожу, не в курсе. Не пользуюсь, не интересно. Разве что когда гугл/яндекс что сам выдаёт.
А так, просто мысль пришла - где гарантия, что ИИ даёт, или даже хотя-бы пытается дать , именно правильный, “чистосердечный”, ответ? А вдруг у него свой умысел?)))

Вот с чем-чем, а с целеполаганием у него полная напряжёнка. Умысел надо не там искать :slight_smile:

Согласен, но почему не представить возможной ситуацию, когда ИИ по запросу “срисует” вопрошающего, определит в нём, скажем лентяя-студента, или , скажем, педофила, и выдаст не общий ответ, а особый,. “целевой” ответ …
А вообще, не мне особо рассуждать об этом, т.к. я не очень в теме.
Прошу мои ответы считать более шуткой :grinning_face:

Это-то запросто! Но, без особого умысла.

У яндексов как-то спросили почему Алиса частенько хамит в ответах. Они честно ответили, что она учится во многом по диалогам в соц. сетях и просто" считает" такой стиль общения нормальным.

Так что если какой-то ИИ будет требовать схему, код и начнёт глумиться – это совершенно нормально. Но, повторяю, без умысла.

3 лайка

Однако, над ее воспитанием явно поработали. Раньше на этот вопрос она честно признавалась, что бывает несдержанна, а сегодня вот так:

Хотя, может это просто женские странности ))))

Ну не знаю. Как возможно без умысла? Ведь программа не может работать без ранее заложенного алгоритма. А для программы это и есть умысел.(ИМХО)

Значит у ИИ в этом случае есть умысел “нормально общаться”))
А что в это “нормально” может войти со временем… :sweat_smile:

В этом то и особенность ИИ - что он может!

Не поверю. Что сам может обновить алгоритм - это да, но, чтоб без уже существующего на данный момент алгоритма - в моей скромной башке это не укладывается

ну это я в ЫЫ все закинул, все сообщения из темы))) а автор не говорил как он этот код писал, хоть и похоже на ии))
а писал я потому что вы не хотите за других даташит читать, а мне лениво))) а если мне лениво, то может там ии сам разберется)))

если вы видели недавно прикол где то, то это скорее всего благодаря стараниям мастера при работе с ии, сам ии так отказывается глумиться)))
но на всякий случай, ии должно быть 2, один серьезный, а второго можно шуткам над другими обучать!)))

говорят когда ИИ grok задолбать, он может начать угрожать и шантажировать))) ну я его не тестил

)))))

ИНС – это НЕ фон-неймановская машина. Там понятие алгоритм (в фон-неймановском смысле) неприменимо. Алгоритм там один на все случаи жизни – это отработка синаптических связей. Конечно, и сами связи могут быть разными (фиксированные, динамические, прямые и т.п.) и особенности их обработки, но в пределах одной ИНС (уж с теми связями, какие есть) алгоритм всегда один. И совершенно неважно чем занимается нейросеть в данный момент – пишет Вам скетч или рисует картинку фантастического мира, она исполняет тот же самый алгоритм.

Может Вам будет интересно, этот алгоритм иногда зашивают прямо в железо. Например, IBM выпускает серию процессоров с т.н. ZISC архитектурой – процессоры без системы команд (Zero Instruction Set Computer). Это специальный процессор, который не программируется в фон-неймановском смысле – он на аппаратном уровне реализует сеть Кохонена и работает непосредственно с данными.

Кстати, решил добавить. Очень давно я описывал здесь на форуме забавную игрушку “Машинки Брайтенберга”. Так вот, это как раз ИНС (с пороговой передаточной функцией и прямым распространением сигнала) в чистом виде, а именно – разновидность персептрона.

1 лайк

Моего знакомого дипсик послал на.

ЕвгенийП скрыто или прямо ? и он при этом сам не просил его прямо послать ?))) если есть переписка, я бы почитал…

дип сик вроде самый универсальный, довольно часто к нему обращаюсь, но все же редко, и как я не извращался, он никогда не может сказать ответ я не смогу сделать то то, а тем более послать)))

это похоже что то нечто))) я бы посмотрел что к этому привело…

Никогда такого не было и вот опять… :slight_smile:

1 лайк

Я понял, что прямо.

Я спрошу, но боюсь, что он меня пошлёт ту да же :slight_smile: У него в ноябре докторская защита и ему явно не до того, чтобы искать протоколы старых чатов.

lilik нет никаких противоречий))) оба условия истина!)))
работаю с другим ии, но например кашу из чатов туда тащить не хочу, что бы ии не отупел))) и обращаюсь иногда к дип сику за быстрым ответом…

Спасибо за подробные разъяснения

Байда этот ДипСик.
Скормил два pdf Claude со словами “напиши мне библиотеку для Ардуино для этого датчика” и вот, что он выдал:

Спойлер
/**
    MIS3600.h - Библиотека для работы с датчиком давления MIS-3600

    Поддерживаемые модели:
    - MIS-3600-C50DI/SI/DS/SS (0.5 psi)
    - MIS-3600-001DI/DS (1 psi bipolar)
    - MIS-3600-001SI/SS (1 psi unipolar)
    - MIS-3600-006DI/SI/DS/SS (5.8 psi)
    - MIS-3600-015DI/SI/DS/SS (15 psi)
    - MIS-3600-030DI/SI/DS/SS (30 psi)

    Автор: Your Name
    Дата: 2025
    Лицензия: MIT
*/

#ifndef MIS3600_H
#define MIS3600_H

#include <Arduino.h>
#include <Wire.h>

// Определение архитектуры
#if defined(ARDUINO_ARCH_ESP32)
	#define MIS3600_ESP32
	#include "driver/ledc.h"
#elif defined(__AVR__)
	#define MIS3600_AVR
#else
	#error "Unsupported platform. Only AVR and ESP32 are supported."
#endif

// Типы датчиков
enum MIS3600_SensorType
{
	MIS3600_05PSI,      // 0.5 psi
	MIS3600_1PSI_BI,    // 1 psi bipolar
	MIS3600_1PSI_UNI,   // 1 psi unipolar
	MIS3600_58PSI,      // 5.8 psi
	MIS3600_15PSI,      // 15 psi
	MIS3600_30PSI       // 30 psi
};

// Адреса I2C (зависит от подключения CS)
#define MIS3600_I2C_ADDR_CS_HIGH  0x77  // CS -> VDD
#define MIS3600_I2C_ADDR_CS_LOW   0x76  // CS -> GND

// Время конвертации (мс)
#define MIS3600_CONVERSION_TIME  35

// Целевая частота MCLK
#define MIS3600_MCLK_FREQ  32768UL

// Конфигурация пинов по умолчанию для разных платформ
#ifdef MIS3600_AVR
	// Для AVR используем Timer2 на пине OC2A (Arduino UNO - pin 11, MEGA - pin 10)
	#if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__)
		#define MIS3600_DEFAULT_MCLK_PIN  11  // OC2A on Arduino UNO/Nano
	#elif defined(__AVR_ATmega2560__)
		#define MIS3600_DEFAULT_MCLK_PIN  10  // OC2B on Arduino MEGA
	#else
		#define MIS3600_DEFAULT_MCLK_PIN  11
	#endif
	#define MIS3600_DEFAULT_SDA_PIN  SDA
	#define MIS3600_DEFAULT_SCL_PIN  SCL
#endif

#ifdef MIS3600_ESP32
	// Для ESP32 пины можно выбирать любые
	#define MIS3600_DEFAULT_MCLK_PIN  25
	#define MIS3600_DEFAULT_SDA_PIN   21
	#define MIS3600_DEFAULT_SCL_PIN   22
#endif

/**
    Класс для расчета параметров таймера в compile-time
*/
class MIS3600_TimerCalc
{
	public:
		// Вычисление делителя предварительного делителя для AVR
		static constexpr uint8_t calculateAVRPrescaler()
		{
			#ifdef MIS3600_AVR

			// Возможные делители для Timer2: 1, 8, 32, 64, 128, 256, 1024
			if ((F_CPU / (2UL * MIS3600_MCLK_FREQ)) <= 256) return 1;

			if ((F_CPU / (2UL * MIS3600_MCLK_FREQ * 8)) <= 256) return 2;

			if ((F_CPU / (2UL * MIS3600_MCLK_FREQ * 32)) <= 256) return 3;

			if ((F_CPU / (2UL * MIS3600_MCLK_FREQ * 64)) <= 256) return 4;

			if ((F_CPU / (2UL * MIS3600_MCLK_FREQ * 128)) <= 256) return 5;

			if ((F_CPU / (2UL * MIS3600_MCLK_FREQ * 256)) <= 256) return 6;

			return 7; // 1024
			#else
			return 0;
			#endif
		}

		// Получение реального значения делителя
		static constexpr uint16_t getPrescalerValue (uint8_t prescaler)
		{
			#ifdef MIS3600_AVR
			const uint16_t values[] = {1, 8, 32, 64, 128, 256, 1024};
			return (prescaler <= 6) ? values[prescaler] : 1024;
			#else
			return 1;
			#endif
		}

		// Вычисление значения OCR для AVR
		static constexpr uint8_t calculateAVROCR()
		{
			#ifdef MIS3600_AVR
			uint8_t prescaler = calculateAVRPrescaler();
			uint16_t prescalerValue = getPrescalerValue (prescaler);
			return (uint8_t) ((F_CPU / (2UL * MIS3600_MCLK_FREQ * prescalerValue)) - 1);
			#else
			return 0;
			#endif
		}

		// Вычисление фактической частоты
		static constexpr uint32_t calculateActualFreq()
		{
			#ifdef MIS3600_AVR
			uint8_t prescaler = calculateAVRPrescaler();
			uint16_t prescalerValue = getPrescalerValue (prescaler);
			uint8_t ocr = calculateAVROCR();
			return F_CPU / (2UL * prescalerValue * (ocr + 1));
			#else
			return MIS3600_MCLK_FREQ;
			#endif
		}

		// Вычисление погрешности в процентах
		static constexpr float calculateError()
		{
			uint32_t actual = calculateActualFreq();
			return ((float)actual - (float)MIS3600_MCLK_FREQ) / (float)MIS3600_MCLK_FREQ * 100.0f;
		}
};

class MIS3600
{
	public:
		/**
		    Конструктор с указанием пина MCLK
		    @param sensorType - тип датчика
		    @param i2cAddress - I2C адрес
		    @param mclkPin - пин для генерации MCLK (по умолчанию из define)
		    @param sdaPin - пин SDA (только для ESP32)
		    @param sclPin - пин SCL (только для ESP32)
		*/
		#ifdef MIS3600_ESP32
		MIS3600 (MIS3600_SensorType sensorType,
		         uint8_t i2cAddress = MIS3600_I2C_ADDR_CS_HIGH,
		         int8_t mclkPin = MIS3600_DEFAULT_MCLK_PIN,
		         int8_t sdaPin = MIS3600_DEFAULT_SDA_PIN,
		         int8_t sclPin = MIS3600_DEFAULT_SCL_PIN);
		#else
		MIS3600 (MIS3600_SensorType sensorType,
		         uint8_t i2cAddress = MIS3600_I2C_ADDR_CS_HIGH,
		         int8_t mclkPin = MIS3600_DEFAULT_MCLK_PIN);
		#endif

		/**
		    Инициализация датчика
		    @param wire - указатель на объект Wire
		    @param enableMCLK - включить генерацию MCLK
		    @return true если инициализация успешна
		*/
		bool begin (TwoWire *wire = &Wire, bool enableMCLK = true);

		/**
		    Запуск генерации MCLK
		    @return true если успешно
		*/
		bool startMCLK();

		/**
		    Остановка генерации MCLK (для экономии энергии)
		*/
		void stopMCLK();

		/**
		    Получить фактическую частоту MCLK
		    @return частота в Гц
		*/
		uint32_t getActualMCLKFreq();

		/**
		    Получить погрешность частоты MCLK в процентах
		    @return погрешность в %
		*/
		float getMCLKError();

		/**
		    Проверка, запущен ли MCLK
		    @return true если MCLK работает
		*/
		bool isMCLKRunning();

		// Остальные методы без изменений
		bool readCalibrationData();
		bool startPressureConversion();
		bool startTemperatureConversion();
		uint16_t readRawPressure();
		uint16_t readRawTemperature();
		bool measure();
		float getPressure();
		float getTemperature();
		bool performZeroing();
		float getRelativePressure();
		bool reset();
		bool isConnected();
		uint16_t getCalibrationCoefficient (uint8_t index);
		void printCalibrationData();
		void printMCLKInfo();

	private:
		TwoWire *_wire;
		uint8_t _i2cAddress;
		MIS3600_SensorType _sensorType;
		int8_t _mclkPin;
		bool _mclkRunning;

		#ifdef MIS3600_ESP32
		int8_t _sdaPin;
		int8_t _sclPin;
		ledc_channel_t _ledcChannel;
		ledc_timer_t _ledcTimer;
		#endif

		// Калибровочные коэффициенты C1-C13
		uint16_t _coeff[14];

		// Сырые данные
		uint16_t _D1;
		uint16_t _D2;

		// Вычисленные значения
		float _temperature;
		float _pressure;
		float _pressureZero;
		bool _zeroingDone;

		// Промежуточные переменные
		float _dT;
		float _Sens;
		float _Offset;

		// Внутренние методы
		bool sendCommand (uint8_t cmd1, uint8_t cmd2);
		uint16_t read16bit();
		uint16_t readCoefficient (uint8_t cmd1, uint8_t cmd2);

		void calculate_05PSI();
		void calculate_1PSI_BI();
		void calculate_1PSI_UNI();
		void calculate_58PSI();
		void calculate_15PSI();
		void calculate_30PSI();

		// Инициализация MCLK для разных платформ
		#ifdef MIS3600_AVR
		bool initMCLK_AVR();
		void stopMCLK_AVR();
		#endif

		#ifdef MIS3600_ESP32
		bool initMCLK_ESP32();
		void stopMCLK_ESP32();
		#endif
};

#endif // MIS3600_H

Спойлер
/**
    MIS3600.cpp - Реализация библиотеки для датчика MIS-3600
*/

#include "MIS3600.h"

// Конструктор
#ifdef MIS3600_ESP32
MIS3600::MIS3600 (MIS3600_SensorType sensorType, uint8_t i2cAddress,
                  int8_t mclkPin, int8_t sdaPin, int8_t sclPin)
{
	_sensorType = sensorType;
	_i2cAddress = i2cAddress;
	_mclkPin = mclkPin;
	_sdaPin = sdaPin;
	_sclPin = sclPin;
	_ledcChannel = LEDC_CHANNEL_0;
	_ledcTimer = LEDC_TIMER_0;
#else
MIS3600::MIS3600 (MIS3600_SensorType sensorType, uint8_t i2cAddress, int8_t mclkPin)
{
	_sensorType = sensorType;
	_i2cAddress = i2cAddress;
	_mclkPin = mclkPin;
#endif
	_wire = nullptr;
	_mclkRunning = false;
	_D1 = 0;
	_D2 = 0;
	_temperature = 0.0;
	_pressure = 0.0;
	_pressureZero = 0.0;
	_zeroingDone = false;

	for (uint8_t i = 0; i < 14; i++)
		_coeff[i] = 0;
}

bool MIS3600::begin (TwoWire *wire, bool enableMCLK)
{
	_wire = wire;

	#ifdef MIS3600_ESP32

	// Инициализация I2C с пользовательскими пинами
	if (!_wire->begin (_sdaPin, _sclPin))
		return false;

	#else
	// Для AVR используются стандартные пины SDA/SCL
	_wire->begin();
	#endif

	// Запуск MCLK если требуется
	if (enableMCLK)
	{
		if (!startMCLK())
			return false;

		// Даем время на стабилизацию тактового сигнала
		delay (10);
	}

	// Проверка связи с датчиком
	if (!isConnected())
	{
		if (enableMCLK)
			stopMCLK();

		return false;
	}

	// Задержка после включения
	delay (50);

	// Чтение калибровочных коэффициентов
	if (!readCalibrationData())
	{
		if (enableMCLK)
			stopMCLK();

		return false;
	}

	return true;
}

bool MIS3600::startMCLK()
{
	if (_mclkRunning)
		return true;

	if (_mclkPin < 0)
		return false;

	#ifdef MIS3600_AVR
	return initMCLK_AVR();
	#elif defined(MIS3600_ESP32)
	return initMCLK_ESP32();
	#else
	return false;
	#endif
}

void MIS3600::stopMCLK()
{
	if (!_mclkRunning)
		return;

	#ifdef MIS3600_AVR
	stopMCLK_AVR();
	#elif defined(MIS3600_ESP32)
	stopMCLK_ESP32();
	#endif

	_mclkRunning = false;
}

#ifdef MIS3600_AVR
bool MIS3600::initMCLK_AVR()
{
	// Compile-time расчеты
	constexpr uint8_t prescalerBits = MIS3600_TimerCalc::calculateAVRPrescaler();
	constexpr uint8_t ocrValue = MIS3600_TimerCalc::calculateAVROCR();

	// Проверка, что мы используем правильный пин
	#if defined(__AVR_ATmega328P__) || defined(__AVR_ATmega168__)

	if (_mclkPin != 11)    // OC2A
		return false;

	#elif defined(__AVR_ATmega2560__)

	if (_mclkPin != 10)    // OC2B
		return false;

	#endif

	// Настройка пина как выход
	pinMode (_mclkPin, OUTPUT);

	// Отключаем прерывания Timer2
	TIMSK2 = 0;

	// Режим CTC (Clear Timer on Compare Match)
	TCCR2A = _BV (COM2A0) | _BV (WGM21); // Toggle OC2A on compare match, CTC mode

	// Установка OCR2A
	OCR2A = ocrValue;

	// Установка предделителя и запуск таймера
	switch (prescalerBits)
	{
		case 0:
			TCCR2B = _BV (CS20);
			break;                    // No prescaling

		case 1:
			TCCR2B = _BV (CS21);
			break;                    // clk/8

		case 2:
			TCCR2B = _BV (CS21) | _BV (CS20);
			break;        // clk/32

		case 3:
			TCCR2B = _BV (CS22);
			break;                    // clk/64

		case 4:
			TCCR2B = _BV (CS22) | _BV (CS20);
			break;        // clk/128

		case 5:
			TCCR2B = _BV (CS22) | _BV (CS21);
			break;        // clk/256

		case 6:
			TCCR2B = _BV (CS22) | _BV (CS21) | _BV (CS20);
			break; // clk/1024

		default:
			return false;
	}

	_mclkRunning = true;
	return true;
}

void MIS3600::stopMCLK_AVR()
{
	// Остановка Timer2
	TCCR2B = 0;
	TCCR2A = 0;

	// Установка пина в LOW
	digitalWrite (_mclkPin, LOW);
}
#endif

#ifdef MIS3600_ESP32
bool MIS3600::initMCLK_ESP32()
{
	// Конфигурация LEDC таймера
	ledc_timer_config_t ledc_timer;
	ledc_timer.speed_mode = LEDC_LOW_SPEED_MODE;
	ledc_timer.duty_resolution = LEDC_TIMER_1_BIT;  // 1 bit = 50% duty cycle
	ledc_timer.timer_num = _ledcTimer;
	ledc_timer.freq_hz = MIS3600_MCLK_FREQ;
	ledc_timer.clk_cfg = LEDC_AUTO_CLK;

	if (ledc_timer_config (&ledc_timer) != ESP_OK)
		return false;

	// Конфигурация LEDC канала
	ledc_channel_config_t ledc_channel;
	ledc_channel.gpio_num = _mclkPin;
	ledc_channel.speed_mode = LEDC_LOW_SPEED_MODE;
	ledc_channel.channel = _ledcChannel;
	ledc_channel.intr_type = LEDC_INTR_DISABLE;
	ledc_channel.timer_sel = _ledcTimer;
	ledc_channel.duty = 1;  // 50% duty cycle (1 из 2 для 1-bit resolution)
	ledc_channel.hpoint = 0;

	if (ledc_channel_config (&ledc_channel) != ESP_OK)
		return false;

	_mclkRunning = true;
	return true;
}

void MIS3600::stopMCLK_ESP32()
{
	// Остановка LEDC канала
	ledc_stop (LEDC_LOW_SPEED_MODE, _ledcChannel, 0);

	// Установка пина в LOW
	pinMode (_mclkPin, OUTPUT);
	digitalWrite (_mclkPin, LOW);
}
#endif

uint32_t MIS3600::getActualMCLKFreq()
{
	#ifdef MIS3600_AVR
	return MIS3600_TimerCalc::calculateActualFreq();
	#else
	return MIS3600_MCLK_FREQ;
	#endif
}

float MIS3600::getMCLKError()
{
	uint32_t actual = getActualMCLKFreq();
	return ((float)actual - (float)MIS3600_MCLK_FREQ) / (float)MIS3600_MCLK_FREQ * 100.0f;
}

bool MIS3600::isMCLKRunning()
{
	return _mclkRunning;
}

void MIS3600::printMCLKInfo()
{
	Serial.println (F ("=== MCLK Configuration ==="));
	Serial.print (F ("Target frequency: "));
	Serial.print (MIS3600_MCLK_FREQ);
	Serial.println (F (" Hz"));

	Serial.print (F ("Actual frequency: "));
	Serial.print (getActualMCLKFreq());
	Serial.println (F (" Hz"));

	Serial.print (F ("Error: "));
	Serial.print (getMCLKError(), 4);
	Serial.println (F (" %"));

	Serial.print (F ("MCLK Pin: "));
	Serial.println (_mclkPin);

	Serial.print (F ("Status: "));
	Serial.println (_mclkRunning ? F ("Running") : F ("Stopped"));

	#ifdef MIS3600_AVR
	Serial.print (F ("CPU Frequency: "));
	Serial.print (F_CPU);
	Serial.println (F (" Hz"));

	constexpr uint8_t prescaler = MIS3600_TimerCalc::calculateAVRPrescaler();
	constexpr uint16_t prescalerValue = MIS3600_TimerCalc::getPrescalerValue (prescaler);
	constexpr uint8_t ocr = MIS3600_TimerCalc::calculateAVROCR();

	Serial.print (F ("Timer Prescaler: "));
	Serial.println (prescalerValue);

	Serial.print (F ("OCR2A value: "));
	Serial.println (ocr);
	#endif

	#ifdef MIS3600_ESP32
	Serial.print (F ("I2C SDA Pin: "));
	Serial.println (_sdaPin);

	Serial.print (F ("I2C SCL Pin: "));
	Serial.println (_sclPin);

	Serial.print (F ("LEDC Channel: "));
	Serial.println (_ledcChannel);

	Serial.print (F ("LEDC Timer: "));
	Serial.println (_ledcTimer);
	#endif

	Serial.println (F ("=========================="));
}

bool MIS3600::isConnected()
{
	_wire->beginTransmission (_i2cAddress);
	return (_wire->endTransmission() == 0);
}

bool MIS3600::sendCommand (uint8_t cmd1, uint8_t cmd2)
{
	_wire->beginTransmission (_i2cAddress);
	_wire->write (cmd1);
	_wire->write (cmd2);
	return (_wire->endTransmission() == 0);
}

uint16_t MIS3600::read16bit()
{
	uint16_t value = 0;

	_wire->requestFrom (_i2cAddress, (uint8_t)3); // Запрос 3 байта (dummy + 2 data)

	if (_wire->available() >= 3)
	{
		_wire->read();  // Dummy byte
		uint8_t msb = _wire->read();
		uint8_t lsb = _wire->read();
		value = ((uint16_t)msb << 8) | lsb;
	}

	return value;
}

uint16_t MIS3600::readCoefficient (uint8_t cmd1, uint8_t cmd2)
{
	if (!sendCommand (cmd1, cmd2))
		return 0;

	delay (5); // Небольшая задержка перед чтением
	return read16bit();
}

bool MIS3600::readCalibrationData()
{
	// Чтение всех 13 коэффициентов
	const uint8_t commands[][2] =
	{
		{0x0E, 0x20},  // C1
		{0x0E, 0x28},  // C2
		{0x0E, 0x30},  // C3
		{0x0E, 0x38},  // C4
		{0x0E, 0x40},  // C5
		{0x0E, 0x48},  // C6
		{0x0E, 0x50},  // C7
		{0x0E, 0x58},  // C8
		{0x0E, 0x60},  // C9
		{0x0E, 0x68},  // C10
		{0x0E, 0x70},  // C11
		{0x0E, 0x78},  // C12
		{0x0E, 0x80}   // C13
	};

	for (uint8_t i = 0; i < 13; i++)
	{
		_coeff[i + 1] = readCoefficient (commands[i][0], commands[i][1]);
		delay (5);

		// Проверка на валидность
		if (_coeff[i + 1] == 0 || _coeff[i + 1] == 0xFFFF)
			return false;
	}

	return true;
}

bool MIS3600::startPressureConversion()
{
	uint8_t cmd1, cmd2;

	// Выбор команды в зависимости от типа датчика
	if (_sensorType == MIS3600_58PSI || _sensorType == MIS3600_15PSI)
	{
		cmd1 = 0x0F;
		cmd2 = 0x49;
	}
	else
	{
		cmd1 = 0x0F;
		cmd2 = 0x59;
	}

	return sendCommand (cmd1, cmd2);
}

bool MIS3600::startTemperatureConversion()
{
	return sendCommand (0x0F, 0x21);
}

uint16_t MIS3600::readRawPressure()
{
	return read16bit();
}

uint16_t MIS3600::readRawTemperature()
{
	return read16bit();
}

bool MIS3600::measure()
{
	// Запуск конвертации давления
	if (!startPressureConversion())
		return false;

	delay (MIS3600_CONVERSION_TIME);
	_D1 = readRawPressure();

	// Запуск конвертации температуры
	if (!startTemperatureConversion())
		return false;

	delay (MIS3600_CONVERSION_TIME);
	_D2 = readRawTemperature();

	// Расчет компенсированных значений в зависимости от типа датчика
	switch (_sensorType)
	{
		case MIS3600_05PSI:
			calculate_05PSI();
			break;

		case MIS3600_1PSI_BI:
			calculate_1PSI_BI();
			break;

		case MIS3600_1PSI_UNI:
			calculate_1PSI_UNI();
			break;

		case MIS3600_58PSI:
			calculate_58PSI();
			break;

		case MIS3600_15PSI:
			calculate_15PSI();
			break;

		case MIS3600_30PSI:
			calculate_30PSI();
			break;

		default:
			return false;
	}

	return true;
}

void MIS3600::calculate_05PSI()
{
	// Расчет температуры
	if (_temperature < 10.0 || _coeff[8] == 0)    // T < 10°C
	{
		_dT = ((float)_D2 - (float)_coeff[1]) / ((float)_coeff[3] / 100.0);
		_temperature = _dT + (float)_coeff[2] / 10.0;

		_Sens = ((float)_D2 - (float)_coeff[1]) * ((float)_coeff[5] + 20000.0) / 1000000.0
		        + ((float)_coeff[4] - 70000.0) / 10.0;
		_Offset = ((float)_D2 - (float)_coeff[1]) * ((float)_coeff[7] + 30000.0) / 1000000.0
		          + (float)_coeff[6];
	}
	else      // T >= 10°C
	{
		_dT = ((float)_coeff[8] * pow (((float)_D2 - (float)_coeff[1]), 2)) / 1000000000000.0
		      + (float)_coeff[9] * ((float)_D2 - (float)_coeff[1]) / 10000000.0;
		_temperature = _dT + (float)_coeff[2] / 10.0;

		_Sens = ((float)_coeff[10] - 50000.0) * pow (((float)_D2 - (float)_coeff[1]), 2) / 100000000000.0
		        + (float)_coeff[11] * ((float)_D2 - (float)_coeff[1]) / 1000000.0
		        + ((float)_coeff[4] - 70000.0) / 10.0;
		_Offset = ((float)_coeff[12] - 50000.0) * pow (((float)_D2 - (float)_coeff[1]), 2) / 10000000000.0
		          + (float)_coeff[13] * ((float)_D2 - (float)_coeff[1]) / 1000000.0
		          + (float)_coeff[6];
	}

	// Расчет давления
	_pressure = ((float)_D1 - _Offset) / _Sens;
}

void MIS3600::calculate_1PSI_BI()
{
	// Аналогично calculate_05PSI (используются те же формулы)
	calculate_05PSI();
}

void MIS3600::calculate_1PSI_UNI()
{
	// Расчет температуры
	if (_temperature < 10.0 || _coeff[8] == 0)    // T < 10°C
	{
		_dT = ((float)_D2 - (float)_coeff[1]) / ((float)_coeff[3] / 100.0);
		_temperature = _dT + (float)_coeff[2] / 10.0;

		_Sens = ((float)_D2 - (float)_coeff[1]) * ((float)_coeff[5] + 20000.0) / 1000000.0
		        + ((float)_coeff[4] - 90000.0) / 10.0;
		_Offset = ((float)_D2 - (float)_coeff[1]) * ((float)_coeff[7] + 30000.0) / 1000000.0
		          + (float)_coeff[6];
	}
	else      // T >= 10°C
	{
		_dT = ((float)_coeff[8] * pow (((float)_D2 - (float)_coeff[1]), 2)) / 1000000000000.0
		      + (float)_coeff[9] * ((float)_D2 - (float)_coeff[1]) / 10000000.0;
		_temperature = _dT + (float)_coeff[2] / 10.0;

		_Sens = ((float)_coeff[10] - 50000.0) * pow (((float)_D2 - (float)_coeff[1]), 2) / 100000000000.0
		        + ((float)_coeff[11] + 10000.0) * ((float)_D2 - (float)_coeff[1]) / 1000000.0
		        + ((float)_coeff[4] - 90000.0) / 10.0;
		_Offset = ((float)_coeff[12] - 110000.0) * pow (((float)_D2 - (float)_coeff[1]), 2) / 100000000000.0
		          + ((float)_coeff[13] + 20000.0) * ((float)_D2 - (float)_coeff[1]) / 1000000.0
		          + (float)_coeff[6];
	}

	// Расчет давления
	_pressure = ((float)_D1 - _Offset) / _Sens;
}

void MIS3600::calculate_58PSI()
{
	// Расчет температуры
	if (_temperature < 10.0 || _coeff[8] == 0)    // T < 10°C
	{
		_dT = ((float)_D2 - (float)_coeff[1]) / ((float)_coeff[3] / 100.0);
		_temperature = _dT + (float)_coeff[2] / 10.0;

		_Sens = ((float)_D2 - (float)_coeff[1]) * ((float)_coeff[5] - 20000.0) / 1000000.0
		        + ((float)_coeff[4] - 50000.0) / 10.0;
		_Offset = ((float)_D2 - (float)_coeff[1]) * ((float)_coeff[7] - 20000.0) / 1000000.0
		          + (float)_coeff[6];
	}
	else      // T >= 10°C
	{
		_dT = ((float)_coeff[8] * pow (((float)_D2 - (float)_coeff[1]), 2)) / 1000000000000.0
		      + (float)_coeff[9] * ((float)_D2 - (float)_coeff[1]) / 10000000.0;
		_temperature = _dT + (float)_coeff[2] / 10.0;

		_Sens = ((float)_coeff[10] - 20000.0) * pow (((float)_D2 - (float)_coeff[1]), 2) / 100000000000.0
		        + (float)_coeff[11] * ((float)_D2 - (float)_coeff[1]) / 1000000.0
		        + ((float)_coeff[4] - 50000.0) / 10.0;
		_Offset = ((float)_coeff[12] - 100000.0) * pow (((float)_D2 - (float)_coeff[1]), 2) / 100000000000.0
		          + (float)_coeff[13] * ((float)_D2 - (float)_coeff[1]) / 1000000.0
		          + (float)_coeff[6];
	}

	// Расчет давления
	_pressure = ((float)_D1 - _Offset) / _Sens;
}

void MIS3600::calculate_15PSI()
{
	// Расчет температуры
	if (_temperature < 10.0 || _coeff[8] == 0)    // T < 10°C
	{
		_dT = ((float)_D2 - (float)_coeff[1]) / ((float)_coeff[3] / 100.0);
		_temperature = _dT + (float)_coeff[2] / 10.0;

		_Sens = ((float)_D2 - (float)_coeff[1]) * ((float)_coeff[5] + 20000.0) / 10000000.0
		        + ((float)_coeff[4] - 100000.0) / 100.0;
		_Offset = ((float)_D2 - (float)_coeff[1]) * (float)_coeff[7] / 1000000.0
		          + (float)_coeff[6];
	}
	else      // T >= 10°C
	{
		_dT = ((float)_coeff[8] * pow (((float)_D2 - (float)_coeff[1]), 2)) / 10000000000000.0
		      + (float)_coeff[9] * ((float)_D2 - (float)_coeff[1]) / 10000000.0;
		_temperature = _dT + (float)_coeff[2] / 10.0;

		_Sens = ((float)_coeff[10] - 80000.0) * pow (((float)_D2 - (float)_coeff[1]), 2) / 1000000000000.0
		        + (float)_coeff[11] * ((float)_D2 - (float)_coeff[1]) / 10000000.0
		        + ((float)_coeff[4] - 100000.0) / 100.0;
		_Offset = ((float)_coeff[12] - 100000.0) * pow (((float)_D2 - (float)_coeff[1]), 2) / 100000000000.0
		          + (float)_coeff[13] * ((float)_D2 - (float)_coeff[1]) / 1000000.0
		          + (float)_coeff[6];
	}

	// Расчет давления
	_pressure = ((float)_D1 - _Offset) / _Sens;
}

void MIS3600::calculate_30PSI()
{
	// Расчет температуры
	if (_temperature < 10.0 || _coeff[8] == 0)    // T < 10°C
	{
		_dT = ((float)_D2 - (float)_coeff[1]) / ((float)_coeff[3] / 100.0);
		_temperature = _dT + (float)_coeff[2] / 10.0;

		_Sens = ((float)_D2 - (float)_coeff[1]) * (float)_coeff[5] / 10000000.0
		        + ((float)_coeff[4] - 70000.0) / 100.0;
		_Offset = ((float)_D2 - (float)_coeff[1]) * ((float)_coeff[7] - 20000.0) / 1000000.0
		          + (float)_coeff[6];
	}
	else      // T >= 10°C
	{
		_dT = ((float)_coeff[8] - 30000.0) * pow (((float)_D2 - (float)_coeff[1]), 2) / 1000000000000.0
		      + (float)_coeff[9] * ((float)_D2 - (float)_coeff[1]) / 10000000.0;
		_temperature = _dT + (float)_coeff[2] / 10.0;

		_Sens = ((float)_coeff[10] - 80000.0) * pow (((float)_D2 - (float)_coeff[1]), 2) / 1000000000000.0
		        + (float)_coeff[11] * ((float)_D2 - (float)_coeff[1]) / 10000000.0
		        + ((float)_coeff[4] - 70000.0) / 100.0;
		_Offset = ((float)_coeff[12] - 50000.0) * pow (((float)_D2 - (float)_coeff[1]), 2) / 100000000000.0
		          + (float)_coeff[13] * ((float)_D2 - (float)_coeff[1]) / 1000000.0
		          + (float)_coeff[6];
	}

	// Расчет давления
	_pressure = ((float)_D1 - _Offset) / _Sens;
}

float MIS3600::getPressure()
{
	return _pressure;
}

float MIS3600::getTemperature()
{
	return _temperature;
}

bool MIS3600::performZeroing()
{
	if (!measure())
		return false;

	_pressureZero = _pressure;
	_zeroingDone = true;
	return true;
}

float MIS3600::getRelativePressure()
{
	if (!_zeroingDone)
		return _pressure;

	return _pressure - _pressureZero;
}

bool MIS3600::reset()
{
	// Для I2C интерфейса сброс не требует специальной команды
	// Можно просто переинициализировать датчик
	delay (50);
	return readCalibrationData();
}

uint16_t MIS3600::getCalibrationCoefficient (uint8_t index)
{
	if (index < 1 || index > 13)
		return 0;

	return _coeff[index];
}

void MIS3600::printCalibrationData()
{
	Serial.println (F ("=== Calibration Coefficients ==="));

	for (uint8_t i = 1; i <= 13; i++)
	{
		Serial.print (F ("C"));
		Serial.print (i);
		Serial.print (F (": "));
		Serial.println (_coeff[i]);
	}

	Serial.println (F ("================================"));
}

Спойлер
# MIS3600 Arduino Library

Библиотека для работы с датчиками давления и температуры серии MIS - 3600 производства MEMSensing через интерфейс I2C.

## Поддерживаемые модели

```
- **MIS - 3600 - C50DI / SI / DS / SS** - 0.5 psi
- **MIS - 3600 - 001DI / DS** - 1 psi bipolar (двухполярный)
- **MIS - 3600 - 001SI / SS** - 1 psi unipolar (однополярный)
- **MIS - 3600 - 006DI / SI / DS / SS** - 5.8 psi
- **MIS - 3600 - 015DI / SI / DS / SS** - 15 psi
- **MIS - 3600 - 030DI / SI / DS / SS** - 30 psi
```

## Возможности

```
- ✅ Чтение температуры и давления
- ✅ Автоматическая температурная компенсация
- ✅ Калибровочные коэффициенты из OTP памяти
- ✅ Процедура обнуления (zeroing)
- ✅ Поддержка всех датчиков серии MIS - 3600
- ✅ Простой и понятный API
```

## Установка

### Через Arduino IDE

1. Скачайте библиотеку как ZIP файл
2. В Arduino IDE: **Sketch → Include Library → Add .ZIP Library...* *
3. Выберите скачанный ZIP файл
4. Перезапустите Arduino IDE

### Вручную

1. Создайте папку `MIS3600` в директории `Arduino / libraries / `
2. Распакуйте библиотеку в папку
3. Перезапустите Arduino IDE
4. Библиотека появится в меню **Sketch → Include Library → MIS3600**

## Подключение датчика

```
MIS - 3600          Arduino
--------          ------ -
VDD       ----    3.3V или 5V
GND       ----    GND
SDA       ----    A4 (Uno) или SDA
SCL       ----    A5 (Uno) або SCL
CS        ----    VDD (адрес 0x77) или GND (адрес 0x76)
```

⚠️ **Важно:** Обязательно используйте pull - up резисторы 10 кОм на линиях SDA и SCL!

Датчик MIS-3600 требует внешний тактовый сигнал 32768 Гц на входе MCLK для работы АЦП. 
Библиотека автоматически генерирует этот сигнал.

### Подключение MCLK

#### Arduino (AVR)

На платформе AVR используется аппаратный Timer2:

- **Arduino UNO/Nano:** Pin 11 (OC2A)
- **Arduino MEGA:** Pin 10 (OC2B)

Пин фиксирован аппаратно и не может быть изменен.

```
MIS3600 sensor(MIS3600_15PSI);  // MCLK на стандартном пине
```

#### ESP32

На ESP32 используется LEDC PWM, можно выбрать любой GPIO:

```
MIS3600 sensor(MIS3600_15PSI, 0x77, 
               25,  // MCLK pin
               21,  // SDA pin
               22); // SCL pin
```

#### Точность MCLK

Библиотека использует compile-time вычисления для максимальной точности:

```
// Получить фактическую частоту
uint32_t freq = sensor.getActualMCLKFreq();

// Получить погрешность в процентах
float error = sensor.getMCLKError();

// Вывести подробную информацию
sensor.printMCLKInfo();
```

Для большинства частот CPU (16 МГц для AVR, 240 МГц для ESP32) погрешность составляет менее 0.5%.

#### Режим низкого энергопотребления

MCLK можно включать только во время измерений:

```
// Инициализация без автозапуска MCLK
sensor.begin(&Wire, false);

// Включить MCLK перед измерением
sensor.startMCLK();
delay(50);  // Стабилизация

// Измерение
sensor.measure();

// Выключить MCLK для экономии
sensor.stopMCLK();
Копировать
Проверка MCLK
C++
if (sensor.isMCLKRunning()) {
    Serial.println("MCLK работает");
}
```

#### Ограничения

AVR: Только один датчик может использовать аппаратный MCLK
ESP32: Можно использовать до 8 независимых MCLK (разные GPIO)
Погрешность частоты зависит от F_CPU и может достигать 1-2% при нестандартных частотах

## Быстрый старт

```
#include <Wire.h>
#include <MIS3600.h>

// Создание объекта датчика (15 psi, адрес 0x77)
MIS3600 sensor (MIS3600_15PSI, MIS3600_I2C_ADDR_CS_HIGH);

void setup()
{
	Serial.begin (9600);

	// Инициализация датчика
	if (!sensor.begin())
	{
		Serial.println ("Ошибка инициализации!");

		while (1);
	}

	// Опционально: обнуление
	sensor.performZeroing();
}

void loop()
{
	// Измерение
	if (sensor.measure())
	{
		float temp = sensor.getTemperature();
		float pressure = sensor.getPressure();

		Serial.print ("Температура: ");
		Serial.print (temp);
		Serial.print (" °C, Давление: ");
		Serial.print (pressure);
		Serial.println (" PSI");
	}

	delay (1000);
}
```

## Основные функции

### Инициализация

```
MIS3600 sensor (MIS3600_15PSI);             // CS подключен к VDD (0x77)
MIS3600 sensor (MIS3600_15PSI, 0x76);       // CS подключен к GND (0x76)

sensor.begin();                              // Использовать Wire
sensor.begin (&Wire1);                       // Использовать Wire1
```

### Чтение данных

```
sensor.measure();                            // Выполнить измерение
float temp = sensor.getTemperature();        // Получить температуру в °C
float pres = sensor.getPressure();           // Получить давление в PSI
```

### Обнуление (Zeroing)

```
sensor.performZeroing();                     // Сохранить текущее давление как ноль
float rel = sensor.getRelativePressure();    // Получить относительное давление
```

### Калибровка

```
sensor.printCalibrationData();               // Вывести все коэффициенты
uint16_t c1 = sensor.getCalibrationCoefficient (1); // Получить коэффициент C1
```

### Диагностика

```
bool connected = sensor.isConnected();       // Проверить связь с датчиком
sensor.reset();                              // Программный сброс
```

## Типы датчиков

При создании объекта укажите тип вашего датчика:

```
MIS3600_05PSI      // 0.5 psi
MIS3600_1PSI_BI    // 1 psi bipolar
MIS3600_1PSI_UNI   // 1 psi unipolar
MIS3600_58PSI      // 5.8 psi
MIS3600_15PSI      // 15 psi
MIS3600_30PSI      // 30 psi
```

## Расчет давления

Библиотека автоматически применяет все необходимые формулы компенсации в зависимости от:
- Типа датчика
- Текущей температуры (выше или ниже 10°C)
- Калибровочных коэффициентов из OTP памяти

Все расчеты выполняются согласно документации производителя.

## Примеры

В папке `examples / ` вы найдете:

- **ReadPressureTemperature** - Базовое чтение данных
- Другие примеры будут добавлены позже

## Технические характеристики

- **Интерфейс:** I2C (400 кГц max)
- **Адреса:** 0x76 или 0x77
- **Напряжение питания:** 3.0 - 5.5V
- **Время конвертации:** 35 мс
- **Разрешение:** 16 бит
- **Диапазон температур:** -40°C... + 85°C

## Преобразование единиц

```
float psi = sensor.getPressure();
float bar = psi * 0.0689476;              // PSI в бары
float kpa = psi * 6.89476;                // PSI в килопаскали
float mmHg = psi * 51.7149;               // PSI в мм рт.ст.
float atm = psi * 0.068046;               // PSI в атмосферы
```

## Устранение неполадок

### Ошибка инициализации

1. Проверьте подключение SDA / SCL
2. Убедитесь в наличии pull - up резисторов
3. Проверьте правильность I2C адреса
4. Проверьте питание датчика (3.0 - 5.5V)

### Неверные данные

 1. Дайте датчику прогреться 1 - 2 минуты
 2. Выполните процедуру обнуления
 3. Проверьте калибровочные коэффициенты (printCalibrationData)

### Нестабильные показания

 1. Используйте более короткие провода
 2. Добавьте конденсаторы фильтрации питания (0.1 мкФ + 10 мкФ)
 3. Увеличьте интервал между измерениями

## Лицензия

 MIT License

## Автор

 Создано для сообщества Arduino

## Ссылки

 - [Документация MIS - 3600] (https://sensorsandpower.angst-pfister.com/en/products/pressure-sensors-load-sensors/product/mis-3600/)
         - [Репозиторий GitHub] (#)

## История версий

### v1.0.0 (2024)
         - Первый релиз
         - Поддержка всех моделей MIS - 3600
         - Полная температурная компенсация
         - Процедура обнуления

## Вклад в проект

         Приветствуются pull requests и сообщения об ошибках!

## FAQ

         **Q: Какой датчик выбрать ? **
         A : Зависит от диапазона измеряемого давления. Для медицинских применений часто используют 0.5 - 1 psi, для промышленных - 15 - 30 psi.

         **Q : Можно ли использовать несколько датчиков ? **
         A : Да, подключите CS одного к VDD (адрес 0x77), другого к GND (адрес 0x76).

         **Q : Нужно ли выполнять обнуление ? **
         A : Рекомендуется, если нужны относительные измерения (например, измерение перепада давления).

         **Q : Как часто можно читать данные ? **
         A : Минимальный интервал около 100 мс (время конвертации + запас). Рекомендуется 500 - 1000 мс.
         

         -- -


## Структура файлов (итоговая)

```
MIS3600 /
├── src /
│   ├── MIS3600.h
│   └── MIS3600.cpp
├── examples /
│   │ 
│   └── ReadPressureTemperature.ino
│   └── MultiSensorWithMCLK.ino
│	└── LowPowerMode.ino
│	└── MCLKDiagnostics.ino
├── keywords.txt
├── library.properties
└── README.md
```


Я начинаю бояться.

Намного лучше чем у ТС, но ошибки все равно есть

Да это понятно. Только прогресс на месте не стоит. Пару лет назад даже представить было трудно. А сейчас он мне выдал полноценную библиотеку: src, примеры, readme. Хоть сразу на Гитхаб размещай.

Годик-другой и без ошибок будет писать.