#include <dev.h> предоставляет доступ к регистрам, входам, выходам, событиям, авариям и планировщику. Индексы начинаются с нуля. Физическое число каналов зависит от модели; текущие и предельные количества можно узнать через countGet() и /json?static → countAll. Константы COUNT_* в текущем заголовке содержат значения X0, поэтому для другой модели сверяйте их с устройством.

  #ifndef __DEV_H
#define __DEV_H

#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>

#ifndef NULL
#define NULL       (0)
#endif

#define bitGet(v, n)    ((v & (1 << (n))) ? 1 : 0)
#define bitOn(v, n)     (v |= (1 << (n)))
#define bitOff(v, n)    (v &= ~(1 << (n)))
#define bitInvert(v, n) (v ^= (1 << (n)))
#define bitSet(v, n, s) (s) ? bitOn(v, (n)) : bitOff(v, (n))

#define ADDR_ALARM (0x4002410C)
#define ADDR_REG   (0x40024110)
#define ADDR_DO    (0x40024190)
#define ADDR_DI    (0x400241B0)
#define ADDR_AI    (0x400241D0)
#define ADDR_AO    (0x40024250)
#define ADDR_PWM   (0x40024290)

#define COUNT_REG  (32)
#define COUNT_DO   (3)
#define COUNT_DI   (5)
#define COUNT_AO   (3)
#define COUNT_AI   (3)

// Получение значения регистра без проверки (макрос)
#define regVal(n)         (*(uint32_t *)(ADDR_REG + n*4))
// Получение значения регистра без проверки, по с приведением типа (макросы)
#define regValBool(n)     (*(bool *)(ADDR_REG + n*4))
#define regValInt(n)      (*(int32_t *)(ADDR_REG + n*4))
#define regValUint(n)     (*(uint32_t *)(ADDR_REG + n*4))
#define regValFloat(n)    (*(float *)(ADDR_REG + n*4))
#define regValEnum(n)     (*(size_t *)(ADDR_REG + n*4))

#define doVal(n)          (*(bool *)(ADDR_DO + n*1))
#define diVal(n)          (*(bool *)(ADDR_DI + n*1))
#define aiVal(n)          (*(float *)(ADDR_AI + (n)*4))
#define aoVal(n)          (*(uint16_t *)(ADDR_AO + n*2))
#define doDutyVal(n)      (*(uint16_t *)(ADDR_PWM + n*2))
#define alarmVal(n)       bitGet(*(uint32_t *)ADDR_ALARM, n)

// Типы элементов для панели управления
typedef enum {
  uiDefault = 0,
  uiInput,
  uiList,
  uiRadio,
  uiButton,
} uiType_t;

// Установка значения регистра без проверки типа
int regSet(uint8_t num, uint32_t value);
// Установка значения регистра с проверкой типа
int regSetBool(uint8_t num, bool value);
int regSetInt(uint8_t num, int32_t value);
int regSetUint(uint8_t num, uint32_t value);
int regSetFloat(uint8_t num, float value);
int regSetEnum(uint8_t num, uint8_t value);

// Получение значения регистра без проверки типа
int regGet(uint8_t num, uint32_t *value);
// Получение значения регистра с проверкой типа
int regGetBool(uint8_t num, bool *value);
int regGetInt(uint8_t num, int32_t *value);
int regGetUint(uint8_t num, uint32_t *value);
int regGetFloat(uint8_t num, float *value);
int regGetEnum(uint8_t num, uint8_t *value);

// Инкремент, декремент и инверсия значения регистра
int regInc(uint8_t num);
int regDec(uint8_t num);
int regToggle(uint8_t num);

// Базовая конфигурация пользовательского регистра
int regConf(const uint8_t num, const char *name, const char *access);
// Расширенная конфигурация для каждого типа
int regConfBool(const uint8_t num, bool defaultValue);
int regConfInt(uint8_t num, int32_t min, int32_t max, uint16_t step, uint8_t dec, int32_t defaultValue);
int regConfUint(uint8_t num, uint32_t min, uint32_t max, uint16_t step, uint8_t dec, uint32_t defaultValue);
int regConfFloat(uint8_t num, float min, float max, float step, uint8_t dec, float defaultValue);
int regConfEnum(uint8_t num, char *items, uint8_t defaultValue);
// Настройка элемента ввода на панели управления
int regConfUi(uint8_t num, uiType_t ui);

// do
int doSet(uint8_t num, bool value);
int doGet(uint8_t num, bool *value);
int doToggle(uint8_t num);
int doSetDuty(uint8_t num, uint16_t duty);
int doGetDuty(uint8_t num, uint16_t *duty);
int doConf(uint8_t num, char *name, char *access, uint16_t pwmPeriodSec);

// di
int diGet(uint8_t num, bool *value);
int diConf(uint8_t num, char *name,  char *access, bool useNormalOpen);

// ao
int aoSet(uint8_t num, uint16_t value);
int aoGet(uint8_t num, uint16_t *value);
int aoInc(uint8_t num);
int aoDec(uint8_t num);
int aoConf(uint8_t num, char *name, char *access);

// ai
int aiGet(uint8_t num, float *value);
int aiConf(uint8_t num, char *name, char *access, int type);

// event
typedef enum {
  eReasonNone,
  eReasonError,
  eReasonAuto,
  eReasonUser,
  eReasonOk,
  eReasonAlgo,
  eReasonTime,
  eReasonWeb,
  eReasonExt,
  eReasonPu,
} eReason_t;

int eventConf(uint8_t num, char *name);
int eventAdd(uint8_t num, void *par1, void *par2, eReason_t *reason);

// alarm
int alarmConf(uint8_t num, char *name);
int alarmOn(uint8_t num, void *par1, void *par2, eReason_t *reason);
int alarmOff(uint8_t num);
int alarmGet(uint8_t num, bool *value);

// others functions
#define micros()        ((*(volatile uint32_t *)0xE0001004) / 168)

typedef enum {
  devDo = 0,
  devDi,
  devAo,
  devAi,
  devReg,
  devEvent,
  devAlarm
} devItem_t;

int countGet(const devItem_t item);
int countSet(const devItem_t item, const size_t count);
bool isChange(const devItem_t item, uint8_t num);

typedef int (*runFn_t)(uint32_t count);
int runAdd(runFn_t fn, uint32_t freq);
int runStart(void);
int run(runFn_t fn, uint32_t freq);

void webconf(bool display, bool reg, bool pin, bool console);

void sleep(uint32_t val);

#endif
  

Общие правила

Указатель результата getter должен указывать на переменную точно того типа, который указан в сигнатуре; NULL отклоняется с -3. При ошибке узкого getter выходная переменная не обновляется. Ошибки API выводятся в консоль и RAM-журнал /txt?log.

name — нуль-терминированное имя ресурса в UTF-8; NULL оставляет имя прежним. Строка access задаёт доступ веб-интерфейса первым символом и внешних служб вторым: - — закрыт, r — чтение, w — чтение и запись. Регистр букв не важен. Для одного символа меняется только веб-доступ. NULL оставляет оба режима прежними, пустая строка разрешает чтение и запись обоим. Например, "wr" разрешает запись из веб-интерфейса и только чтение внешним службам. Сценарий сам не ограничивается этими флагами.

Конфигурирующие функции меняют текущие настройки ресурса, но не выполняют отдельную команду сохранения настроек во flash. Вызов с ошибкой не гарантирует отката уже выполненных изменений: проверяйте параметры заранее и анализируйте код возврата.

Пользовательские регистры

Доступны 32 регистра, каждый хранит 32 бита. Активное количество задаётся countSet(devReg, count). Тип устанавливается функциями regConfBool/Int/Uint/Float/Enum; отдельного публичного regType_t в dev.h нет.

Для int/uint поле dec задаёт десятичный масштаб отображения: C getter/setter работает с хранимым целым без автоматического умножения или деления. Например, при dec=1 сырое значение 235 отображается как 23,5. Float передаётся непосредственно как float.

regSet

  int regSet(uint8_t num, uint32_t value);
  

Передаёт 32-битное представление значения без проверки совпадения типа функции с типом регистра. Ограничения самого регистра проверяются. Возвращает 0 при успехе, в том числе если значение совпадало; -1 при неверном индексе (также возможен для неверного enum), -2 при нарушении числовых ограничений, -3 при неподдерживаемом типе. Для float используйте regSetFloat(), а не числовое приведение float к uint32_t.

regSetBool

  int regSetBool(uint8_t num, bool value);
  

Записывает значение с проверкой типа регистра. Для enum передаётся индекс варианта, начиная с нуля. Возвращает 0 при успехе, -1 при несовпадении типа, -2 при неверном индексе регистра; ошибки проверки значения нижнего уровня также передаются вызывающему коду. Эти коды могут совпадать, поэтому при диагностике учитывайте тип и границы значения.

regSetInt

  int regSetInt(uint8_t num, int32_t value);
  

Записывает значение с проверкой типа регистра. Для enum передаётся индекс варианта, начиная с нуля. Возвращает 0 при успехе, -1 при несовпадении типа, -2 при неверном индексе регистра; ошибки проверки значения нижнего уровня также передаются вызывающему коду. Эти коды могут совпадать, поэтому при диагностике учитывайте тип и границы значения.

regSetUint

  int regSetUint(uint8_t num, uint32_t value);
  

Записывает значение с проверкой типа регистра. Для enum передаётся индекс варианта, начиная с нуля. Возвращает 0 при успехе, -1 при несовпадении типа, -2 при неверном индексе регистра; ошибки проверки значения нижнего уровня также передаются вызывающему коду. Эти коды могут совпадать, поэтому при диагностике учитывайте тип и границы значения.

regSetFloat

  int regSetFloat(uint8_t num, float value);
  

Записывает значение с проверкой типа регистра. Для enum передаётся индекс варианта, начиная с нуля. Возвращает 0 при успехе, -1 при несовпадении типа, -2 при неверном индексе регистра; ошибки проверки значения нижнего уровня также передаются вызывающему коду. Эти коды могут совпадать, поэтому при диагностике учитывайте тип и границы значения.

regSetEnum

  int regSetEnum(uint8_t num, uint8_t value);
  

Записывает значение с проверкой типа регистра. Для enum передаётся индекс варианта, начиная с нуля. Возвращает 0 при успехе, -1 при несовпадении типа, -2 при неверном индексе регистра; ошибки проверки значения нижнего уровня также передаются вызывающему коду. Эти коды могут совпадать, поэтому при диагностике учитывайте тип и границы значения.

regGet

  int regGet(uint8_t num, uint32_t *value);
  

Читает сырые 32 бита. Возвращает 0, либо -1 для неверного индекса и -3 для NULL. Для float предпочтителен regGetFloat().

regGetBool

  int regGetBool(uint8_t num, bool *value);
  

Читает значение в переменную соответствующего типа. Возвращает 0, -1 при несовпадении типа, -2 при неверном индексе и -3 для NULL. Enum возвращается как индекс варианта uint8_t.

regGetInt

  int regGetInt(uint8_t num, int32_t *value);
  

Читает значение в переменную соответствующего типа. Возвращает 0, -1 при несовпадении типа, -2 при неверном индексе и -3 для NULL. Enum возвращается как индекс варианта uint8_t.

regGetUint

  int regGetUint(uint8_t num, uint32_t *value);
  

Читает значение в переменную соответствующего типа. Возвращает 0, -1 при несовпадении типа, -2 при неверном индексе и -3 для NULL. Enum возвращается как индекс варианта uint8_t.

regGetFloat

  int regGetFloat(uint8_t num, float *value);
  

Читает значение в переменную соответствующего типа. Возвращает 0, -1 при несовпадении типа, -2 при неверном индексе и -3 для NULL. Enum возвращается как индекс варианта uint8_t.

regGetEnum

  int regGetEnum(uint8_t num, uint8_t *value);
  

Читает значение в переменную соответствующего типа. Возвращает 0, -1 при несовпадении типа, -2 при неверном индексе и -3 для NULL. Enum возвращается как индекс варианта uint8_t.

regVal

  #define regVal(n)         (*(uint32_t *)(ADDR_REG + n*4))
  

Прямое обращение к памяти без проверки индекса и конфигурации. Макрос может использоваться как lvalue, но такая запись обходит проверки диапазона. Предпочитайте проверяемые функции. regValEnum() в текущем заголовке обращается к size_t, то есть ко всем 32 битам регистра.

regValBool

  #define regValBool(n)     (*(bool *)(ADDR_REG + n*4))
  

Прямое обращение к памяти без проверки индекса и конфигурации. Макрос может использоваться как lvalue, но такая запись обходит проверки диапазона. Предпочитайте проверяемые функции. regValEnum() в текущем заголовке обращается к size_t, то есть ко всем 32 битам регистра.

regValInt

  #define regValInt(n)      (*(int32_t *)(ADDR_REG + n*4))
  

Прямое обращение к памяти без проверки индекса и конфигурации. Макрос может использоваться как lvalue, но такая запись обходит проверки диапазона. Предпочитайте проверяемые функции. regValEnum() в текущем заголовке обращается к size_t, то есть ко всем 32 битам регистра.

regValUint

  #define regValUint(n)     (*(uint32_t *)(ADDR_REG + n*4))
  

Прямое обращение к памяти без проверки индекса и конфигурации. Макрос может использоваться как lvalue, но такая запись обходит проверки диапазона. Предпочитайте проверяемые функции. regValEnum() в текущем заголовке обращается к size_t, то есть ко всем 32 битам регистра.

regValFloat

  #define regValFloat(n)    (*(float *)(ADDR_REG + n*4))
  

Прямое обращение к памяти без проверки индекса и конфигурации. Макрос может использоваться как lvalue, но такая запись обходит проверки диапазона. Предпочитайте проверяемые функции. regValEnum() в текущем заголовке обращается к size_t, то есть ко всем 32 битам регистра.

regValEnum

  #define regValEnum(n)     (*(size_t *)(ADDR_REG + n*4))
  

Прямое обращение к памяти без проверки индекса и конфигурации. Макрос может использоваться как lvalue, но такая запись обходит проверки диапазона. Предпочитайте проверяемые функции. regValEnum() в текущем заголовке обращается к size_t, то есть ко всем 32 битам регистра.

regInc

  int regInc(uint8_t num);
  

Меняет числовой регистр на настроенный шаг; при выключенном шаге используется 1. Для enum шаг равен одному индексу. Bool отклоняется. Целочисленное переполнение и выход за границы не приводят к циклическому оборачиванию: возвращается ошибка. 0 — изменение, возможен 1 при совпадении результата, отрицательное значение — отказ.

regDec

  int regDec(uint8_t num);
  

Меняет числовой регистр на настроенный шаг; при выключенном шаге используется 1. Для enum шаг равен одному индексу. Bool отклоняется. Целочисленное переполнение и выход за границы не приводят к циклическому оборачиванию: возвращается ошибка. 0 — изменение, возможен 1 при совпадении результата, отрицательное значение — отказ.

regToggle

  int regToggle(uint8_t num);
  

Инвертирует младший бит сырых 32 бит (value ^= 1). Тип и min/max не проверяет; используйте для логического регистра. Это не арифметическое отрицание float и не переключение произвольного enum. 0 — успех, -1 — неверный индекс.

regConf

  int regConf(const uint8_t num, const char *name, const char *access);
  

Задаёт имя и доступ, сохраняя тип. Аргумента типа здесь нет. 0 — успех; -1 — индекс, -2 — имя, -3 — access.

regConfBool

  int regConfBool(const uint8_t num, bool defaultValue);
  

Выбирает тип bool. Если старое сырое значение не равно 0 или 1, заменяет его defaultValue. Допустимое старое значение сохраняется; default не присваивается безусловно. 0 — успех; -1 — индекс, -5 — недопустимый default.

regConfInt

  int regConfInt(uint8_t num, int32_t min, int32_t max, uint16_t step, uint8_t dec, int32_t defaultValue);
  

Границы — полные int32_t. min == max отключает обе границы; при включённых границах требуется min < max. Шаг uint16_t: 0 выключает настроенный шаг, иначе шаг должен быть меньше max-min. dec допускается от 0 до 5. Default проверяется и записывается только если прежнее значение не подходит. 0 — успех; -1 — индекс, -2 — границы, -3 — шаг, -4 — dec, -5 — default.

regConfUint

  int regConfUint(uint8_t num, uint32_t min, uint32_t max, uint16_t step, uint8_t dec, uint32_t defaultValue);
  

Аналог regConfInt с границами uint32_t. Диапазон до 4294967295; шаг по-прежнему uint16_t. В этой реализации код -4 используется и для неверного dec, и для недопустимого default; -1/-2/-3 означают индекс/границы/шаг.

regConfFloat

  int regConfFloat(uint8_t num, float min, float max, float step, uint8_t dec, float defaultValue);
  

Границы, шаг и default передаются как float; min == max отключает ограничения. step == 0 выключает настроенный шаг; задавайте положительный конечный шаг. Шаг хранится в компактном формате и может округляться. dec от 0 до 5 влияет на отображение. 0 — успех; -1/-2/-3/-4/-5 — индекс/границы/шаг/dec/default.

regConfEnum

  int regConfEnum(uint8_t num, char *items, uint8_t defaultValue);
  

Варианты задаются одной нуль-терминированной строкой с разделителем ;, например "Off;On;Auto". Индексы — 0, 1, 2; завершающий ; не добавляйте. Используйте от 1 до 256 вариантов и не допускайте пустых элементов. Для хранения текста используется общий ограниченный пул имён. Default заменяет старое значение только при его недопустимости. 0 — успех; -1 — индекс, -2 — запись строки, -3 — пустая строка, -5 — default.

regConfUi

  typedef enum {
  uiDefault = 0,
  uiInput,
  uiList,
  uiRadio,
  uiButton,
} uiType_t;
int regConfUi(uint8_t num, uiType_t ui);
  

Выбирает элемент пользовательской панели, не меняя тип и значение. Имена констант: uiDefault, uiInput, uiList, uiRadio, uiButton. 0 — успех, -1 — индекс.

Пример конфигурации регистра

Пример меняет настройки регистра 0 и его значение.

  #include <dev.h>
#include <stdio.h>

int main(void) {
    if (countSet(devReg, 1) < 0) {return 1;}
    if (regConf(0, "Temperature", "wr") < 0) {return 1;}
    if (regConfInt(0, -400, 1000, 5, 1, 200) < 0) {return 1;}
    if (regSetInt(0, 235) < 0) {return 1;}
    int32_t raw;
    if (regGetInt(0, &raw) < 0) {return 1;}
    printf("Temperature: %.1f\n", (double)raw / 10.0);
    return 0;
}
  

Дискретные выходы

Состояние Do передаётся как bool. PWM управляется отдельно: duty=0..1000 соответствует 0..100 %; физическое число PWM-каналов зависит от модели (X0: один, X1: пять). Это не число всех Do.

doConf

  int doConf(uint8_t num, char *name, char *access, uint16_t pwmPeriodSec);
  

Имя, доступ и режим Do. pwmPeriodSec=0 выбирает обычный выход; ненулевое значение включает программный PWM с периодом в секундах. Применяйте PWM только на поддерживающих его каналах. Включает флаг en. 0 — успех; -1/-2/-3 — индекс/имя/access.

doSet

  int doSet(uint8_t num, bool value);
  

Записывает логическое состояние активного Do. 0 — изменён, 1 — уже имел это значение, -1 — индекс, отрицательный код — отказ.

doGet

  int doGet(uint8_t num, bool *value);
  

Читает bool. 0 — успех; -1 — физический индекс, -3 — NULL.

doToggle

  int doToggle(uint8_t num);
  

Переключает состояние Do. 0 — успех, -1 — индекс. Для аппаратного управления состоянием предпочтителен doSet(): текущая реализация toggle меняет значение в памяти.

doSetDuty

  int doSetDuty(uint8_t num, uint16_t duty);
  

Записывает заполнение PWM от 0 до 1000. 0 — изменён, 1 — уже совпадал, -1 — индекс PWM, -2 — диапазон, -3 — Do не настроен на PWM.

doGetDuty

  int doGetDuty(uint8_t num, uint16_t *duty);
  

Читает заполнение в uint16_t. 0 — успех, -1 — индекс PWM, -3 — NULL.

doVal

  #define doVal(n)          (*(bool *)(ADDR_DO + n*1))
  

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

doDutyVal

  #define doDutyVal(n)      (*(uint16_t *)(ADDR_PWM + n*2))
  

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

Дискретные входы

diConf

  int diConf(uint8_t num, char *name,  char *access, bool useNormalOpen);
  

Задаёт имя, access и нормальное состояние входа (useNormalOpen записывается в конфигурацию). Включает en. 0 — успех, -1 — индекс; -2 используется и для ошибки имени, и для access. Поведение при физическом замыкании зависит от этой настройки.

diGet

  int diGet(uint8_t num, bool *value);
  

Читает логическое состояние в bool. 0 — успех; -1 — физический индекс, -3 — NULL.

diVal

Макрос diVal(n) читает bool напрямую, без проверки физического индекса.

Аналоговые выходы

Ao хранит uint16_t от 0 до 1000: значение 1000 соответствует 10 В, 235 — 2,35 В. C API не выполняет автоматическое масштабирование.

aoConf

  int aoConf(uint8_t num, char *name, char *access);
  

Задаёт имя и доступ, включает en. 0 — успех; -1/-2/-3 — индекс/имя/access.

aoSet

  int aoSet(uint8_t num, uint16_t value);
  

Задаёт значение в сотых долях вольта. 0 — изменён, 1 — совпадал, -1 — индекс активного Ao, -3 — диапазон.

aoGet

  int aoGet(uint8_t num, uint16_t *value);
  

Читает сырое значение в uint16_t. 0 — успех; -1 — физический индекс, -3 — NULL.

aoInc

  int aoInc(uint8_t num);
  

Увеличивает сырое значение на 10, то есть на 0,1 В. 0 — успех, отрицательный код — индекс или диапазон.

aoDec

  int aoDec(uint8_t num);
  

Уменьшает сырое значение на 10, то есть на 0,1 В. Выход за допустимый диапазон возвращает ошибку.

aoVal

Макрос aoVal(n) читает uint16_t из памяти. Для записи используйте aoSet(), чтобы проверить диапазон.

Аналоговые входы

Ai хранит и возвращает float (32 бита) в физических единицах: температура NTC10K — в °C, сопротивление — в омах, а поддерживаемый режим напряжения X1 — в вольтах. Например, 23.5f — 23,5 °C, а 10000.0f в режиме сопротивления — 10 кОм. Повторное деление результата на 10 или 100 не требуется.

При ошибке измерения значение — NaN. Это отличается от старого целочисленного формата: проверка на 32767 / 0x7FFF для aiGet() больше не применяется. Маркер 0x7FFF относится к целочисленному представлению внешних протоколов, а не к C API.

После обновления прошивки перекомпилируйте C-сценарии. Вызов нового aiGet() со старым указателем int16_t * недопустим: функция записывает четыре байта, поэтому такой вызов может повредить соседнюю память.

aiConf

  int aiConf(uint8_t num, char *name, char *access, int type);
  

type=0 — сопротивление в омах, type=1 — температура NTC10K в °C. Имя и access имеют общую семантику; канал включается. 0 — успех; -1/-2/-3/-4 — индекс/имя/access/type.

На X1 предусмотрен отдельный режим напряжения (type=2), но его настройка пока не согласована со всеми способами проверки и сохранения конфигурации. Не считайте type=0 или type=1 режимом 0–10 В и перед использованием напряжения проверьте поддержку в вашей версии прошивки. Хотя функция принимает значения 0…5, варианты 3…5 не задают поддерживаемые дополнительные режимы измерения.

aiGet

  int aiGet(uint8_t num, float *value);
  

Записывает float в переданную переменную. 0 — успешно прочитано; -1 — физический индекс, -3 — NULL. При отрицательном коде результат не использовать. Успех чтения API сам по себе не означает исправность датчика: затем проверьте isfinite(value) или isnan(value) из <math.h>.

  #include <stdio.h>
#include <math.h>
#include <dev.h>

static int showTemperature(uint32_t count) {
    (void)count;
    float temperature;
    int result = aiGet(0, &temperature);
    if (result < 0) {
        printf("Ai read error: %d\n", result);
        return -1;
    }
    if (!isfinite(temperature)) {
        puts("Sensor error");
        return 0;
    }
    printf("Temperature: %.2f C\n", (double)temperature);
    return 0;
}

int main(void) {
    if (countSet(devAi, 1) < 0) {return 1;}
    if (aiConf(0, "Temperature", "rr", 1) < 0) {return 1;}
    return run(showTemperature, 1000);
}
  

aiVal

  #define aiVal(n) (*(float *)(ADDR_AI + (n) * 4))
  

Прямое чтение float из памяти, четыре байта на канал, без проверки индекса. Для прикладного кода предпочтителен aiGet(). Адреса массивов зависят от формата данных прошивки: используйте заголовок, полученный вашим компилятором от контроллера, а не адреса из старого сценария. Запись через aiVal() не поддерживается.

События и аварии

Для Event и Alarm предварительно задайте активное количество через countSet(). Их номера независимы. События записываются в журнал; Alarm дополнительно имеет текущее состояние.

eventConf

  int eventConf(uint8_t num, char *name);
  

Задаёт имя события. 0 — успех; -1 — индекс, -2 — имя.

eventAdd

  int eventAdd(uint8_t num, void *par1, void *par2, eReason_t *reason);
  

par1/par2 — NULL или указатели на доступные 32-битные значения, а не сами числа. reason — NULL или указатель на eReason_t (eReasonNone, eReasonError, eReasonAuto, eReasonUser, eReasonOk, eReasonAlgo, eReasonTime, eReasonWeb, eReasonExt, eReasonPu). Не передавайте указатель на uint8_t/uint16_t вместо параметра. Записывает событие. 0 — успех; -1 — индекс, -3 — причина или журнал.

alarmOn

  int alarmOn(uint8_t num, void *par1, void *par2, eReason_t *reason);
  

par1/par2 — NULL или указатели на доступные 32-битные значения, а не сами числа. reason — NULL или указатель на eReason_t (eReasonNone, eReasonError, eReasonAuto, eReasonUser, eReasonOk, eReasonAlgo, eReasonTime, eReasonWeb, eReasonExt, eReasonPu). Не передавайте указатель на uint8_t/uint16_t вместо параметра. Записывает аварийное событие и включает бит Alarm. 0 — успех; -1 — индекс, -3 — причина/журнал, -4 — установка состояния.

alarmConf

  int alarmConf(uint8_t num, char *name);
  

Задаёт имя аварии. 0 — успех; -1 — индекс, -2 — имя.

alarmOff

  int alarmOff(uint8_t num);
  

Сбрасывает бит Alarm, без отдельной записи события выключения в этой функции. 0 — успех, отрицательное значение — ошибка индекса или установки.

alarmGet

  int alarmGet(uint8_t num, bool *value);
  

Читает текущее состояние в bool. 0 — успех, -1 — индекс, -3 — NULL.

alarmVal

Макрос alarmVal(n) извлекает бит из общей 32-битной маски Alarm без проверки номера. Это выражение чтения; для изменения используйте alarmOn()/alarmOff().

Количество ресурсов и изменения

Параметр devItem_t принимает devDo, devDi, devAo, devAi, devReg, devEvent или devAlarm. Отдельного значения для PWM в этом enum нет.

countSet

  int countSet(const devItem_t item, const size_t count);
  

Задаёт активное количество от 0 до физического/логического предела. Для входов/выходов также меняет en у каналов. 0 — успех, -1 — неверный вид ресурса или превышение максимума. Уменьшение не является командой очистки старых значений.

countGet

  int countGet(const devItem_t item);
  

Возвращает активное количество либо -1 при неизвестном виде ресурса. Для чтения физического предела используйте данные модели в /json?static.

isChange

  bool isChange(const devItem_t item, uint8_t num);
  

Сравнивает значение с последним снимком: true — изменилось, false — не изменилось или произошла ошибка. Ошибка журналируется. Снимок обновляется при проверке; это не очередь всех произошедших изменений. Поддерживаются значения Do, Di, Ao, Ai и Reg. Alarm и Event в этой функции не поддерживаются и дают false с записью ошибки.

Планировщик и интерфейс

runFn_t — int (*)(uint32_t count). Callback вызываются последовательно в одной задаче; это не параллельные потоки. count начинается с нуля. Возврат -1 удаляет callback из расписания, любой другой результат оставляет его.

run

  int run(runFn_t fn, uint32_t freq);
  

Регистрирует один callback и блокируется в runStart(). freq — период в миллисекундах от 1 до INT32_MAX; не частота в герцах. Вложенный запуск запрещён. Функция возвращается, когда callback удалены или запрошена внешняя остановка.

runAdd

  int runAdd(runFn_t fn, uint32_t freq);
  

Добавляет callback в один из восьми слотов, но не запускает цикл. 0 — успех; -1 — NULL или нет свободного слота, -2 — период. Проверка периода и указателя выполняется до регистрации.

runStart

  int runStart(void);
  

Начинает цикл зарегистрированных callback; первый вызов планируется через их период. Период отсчитывается от фактического начала предыдущего вызова, пропущенные периоды не воспроизводятся пачкой. Просроченные callback выбираются по величине просрочки; переполнение millis учитывается. Медленный callback задерживает остальные. 0 — цикл завершён, -1 — уже выполняется.

sleep

  void sleep(uint32_t val);
  

Приостанавливает задачу сценария на заданное число миллисекунд (тиков в текущей конфигурации). При вызове из callback задерживает весь его планировщик.

webconf

  void webconf(bool display, bool reg, bool pin, bool console);
  

Управляет видимостью блоков display/reg/pin/console в пользовательской панели. Не является настройкой прав доступа к самим значениям.

Время

Функции millis() и timePowerOff() объявлены в tools.h; подключайте его явно. micros() — макрос из dev.h.

millis

uint32_t millis(void) возвращает счётчик миллисекунд sysTime, который оборачивается через 2³² мс (около 49,7 суток). Интервалы вычисляйте как (uint32_t)(millis() - start), а не сравнением абсолютных дедлайнов.

micros

micros() читает DWT_CYCCNT и делит на 168. Это текущее значение 32-битного счётчика тактов, а не неограниченный uptime в микросекундах: при частоте 168 МГц оно скачком возвращается к нулю примерно через 25,6 с. Для измерения короткого интервала вычтите исходные значения clock() в uint32_t и затем разделите на 168.

timePowerOff

uint32_t timePowerOff(void) возвращает длительность предыдущего отключения в секундах. При отсутствии пригодной сохранённой метки возможен UINT32_MAX; это не время работы текущего сценария.

Битовые макросы

Все перечисленные макросы из dev.h используют выражение 1 << n. Передавайте простую целочисленную переменную и индекс в пределах её разрядности. Для старшего бита uint32_t предпочтительно явное 1U << n: знаковый литерал 1 в текущих макросах не даёт корректного общего контракта для n=31. Аргументы со сменой состояния и сложные выражения не используйте.

bitGet

  #define bitGet(v, n)    ((v & (1 << (n))) ? 1 : 0)
  

Возвращает 0 или 1 для выбранного бита.

bitOn

  #define bitOn(v, n)     (v |= (1 << (n)))
  

Устанавливает бит в переменной.

bitOff

  #define bitOff(v, n)    (v &= ~(1 << (n)))
  

Сбрасывает бит.

bitInvert

  #define bitInvert(v, n) (v ^= (1 << (n)))
  

Инвертирует бит.

bitSet

  #define bitSet(v, n, s) (s) ? bitOn(v, (n)) : bitOff(v, (n))
  

Устанавливает либо сбрасывает бит по истинности третьего аргумента.