• Система автоматизации с открытым исходным кодом на базе esp8266/esp32 микроконтроллеров и приложения IoT Manager. Наша группа в Telegram

UBIA - USB / BLE to I2C/SMBus Adapter.

pvvx

Активный участник сообщества
речь вот о чем

uint8_t size; //+0 размер данных пакета
uint8_t cmd; //+1 номер команды / тип пакета (=1)

Этот номер команды может быть любым числом или обязательно 0xC как указано тут?


Read(event,reg_addr, len)
{
let blk = new Uint8Array(reg_addr.byteLength + 5);
blk[0] = blk.byteLength - 2;
blk[1] = 0x0C;
blk[2] = reg_addr.byteLength;
blk[3] = (len & 0x7f) | 0x80;



Другими словами, можно сделать 0xC для получения времени и 0xD для получения температуры?
Для данной функции (чтение/запись i2c/SMBUS) в данной прошивке всегда 0x0С.

А так примерно описываются 80% пакетов в данном ящике :)
 
Для данной функции (чтение/запись i2c/SMBUS) в данной прошивке всегда 0x0С.
Понял. и никаких данных в пакет не всунуть, чтобы они обратно вернулись как маркер?
тоесть только "флажки" отправил данные взвел флажок, при первом возврате сбросил.
 

pvvx

Активный участник сообщества
Понял. и никаких данных в пакет не всунуть, чтобы они обратно вернулись как маркер?
тоесть только "флажки" отправил данные взвел флажок, при первом возврате сбросил.
Я вам писал - в js есть async–функций c await promise...
 

pvvx

Активный участник сообщества
C USB-UART в эксплорере без async вообще никак не выходит...
JavaScript:
function delay(ms) {
    return new Promise((resolve, reject) => {
        setTimeout(resolve, ms);
    });
}
 

pvvx

Активный участник сообщества
Ныне то любой примитивный датчик с пару ножек имеет примерно такую простыню.
Пусть дитяти приучаются в Arduino к таким столбикам. Но там ещё нет вложенного столбика к каждому значению адреса с побитными значениями. :(
Вы уже тоже с этим столкнулись...

Так что:
 
Пусть дитяти приучаются в Arduino к таким столбикам.
Чем то это напоминает неуменую борьбу с goto в свое время. Я так с места даже не скажу что понятнее такая простыня констант или сами изначальные hex коды.
async await вещь хорошая, только изначальная идеология странички пострадает. Подумаю как лучше сделать.
Ныне то любой примитивный датчик с пару ножек
Он кстати не очень то и примитивный, скорее даже довольно заумный :) И adafruit к слову библиотека получше, только она еще больше в размере.
 
Я вам писал - в js есть async–функций c await promise...
Что то я совсем запутался, как они нам смогут понять разобрать что пришло. Вот код обработки I2c часов, обратите внимание на комментарии


JavaScript:
    Loop(e, idx, value) {
        this._ble = e.ble;
        let ds = value.getUint8(0);
        if (idx == 0x0C && ds >= 2) {
            if (this.stage_read > 0) {
                if (this._new_date) {
                    this.log("set new date")
                    this.Write(e, new Uint8Array([0x0]), this._new_date).then(a=>{
                        this._new_date = null;
                    });
                } else {
                    this.Read(e, new Uint8Array([0]), 7).then(a=>{  // читаю дату время
                        if (this.stage_read > 1 && ds >= 9) {
                            if (this.datetimeChanged) { // если подключен обработчик событий
                                let seconds = this._decode(value.getUint8(4));
                                let minutes = this._decode(value.getUint8(5));
                                 ...
                                this.datetimeChanged(new Date(year, month, date, hours, minutes, seconds, 0));
                            }
                            this.Read(e, new Uint8Array([0x11]), 2).then(a=>{ // читаю два байта температуры из 0x11 регистра
                                // да функция отработала вслед за первым Read
                                // но данные то еще не пришли????
                                let _msb = value.getUint8(4);  // тут у нас по прежнему секунды
                                let _lsb = value.getUint8(5);  // а тут по прежнему минуты
                                this.tempChanged(_msb + ((_lsb >> 6) * 0.25));  
                            });
                        }
                    });
                }
                this.stage_read = 2;
            }
        }
    }
 

pvvx

Активный участник сообщества
Он кстати не очень то и примитивный, скорее даже довольно заумный :) И adafruit к слову библиотека получше, только она еще больше в размере.
Это STM - вместо 2-х регистров с установкой скорости-фильтра и времени замера желают чтобы пользователи написали за них программу во встроенный микроконтроллер и главное побайтно в шахматном порядке загнали туда...
 

pvvx

Активный участник сообщества
Что то я совсем запутался, как они нам смогут понять разобрать что пришло. Вот код обработки I2c часов, обратите внимание на комментарии
Пришло то, на что вы послали запрос.
Т.е. все переменные и индексы вам известны.
Постоянное применение loop() тут и сказывается. Если вы так обречены на loop(), то стройте Машину Тьюринга.
Там и машины Т не требуется - простой массив-список функций с поочередным исполнением :)
 

pvvx

Активный участник сообщества
Подключил зачем-то платку с али с DAC MCP4725, выход пустил на вход ADC.
Получил такую кривулину:
1615307339026.png
Фильтрация питания у DAC нулевая, как и писано в доках.
1615307375448.png
Бьет в js вывод в DAC хотя по Date().getTime() с шагом 100 ms прибавляется и отправляется значение вывода в DAC.
Но каждый шаг 12 битного DAC виден (делить ~3, т.к. у DAC 0..3.3 В, а у ADC 0..1.3 В).
 
Пришло то, на что вы послали запрос.
Дык и я о чем. Если я все правильно понял, await-async нам никак не поможет определить адресата сообщения, так как ответ не дается немедленно как результат функции Write или Read. Он приходит "сильно потом", когда чип на шине обработает ваш запрос и сформирует ответное сообщение. Примерно такая-же фигня происходит в шлюзах платежных систем PayPal или Yandex. Тамошние ребята борются с ней формируя ID трансакции, возвращая его при вызове функции "Write" и затем включая его в тело "отложенного" ответа. Как я понял у нас в прошивке ничего подобного нет и поэтому определять адресата сообщения можно только косвенно по ds ибо idx(который всегда равен 0x0C).

Как я понял, в первоначальной задумке стандарта ble устройство должно плодить сами characteristic, следуя правилу одна характеристика == одна функция. Хочешь получать от чипа DateTime - сделай специальную характеристику, хочешь получать от него еще и температуру - сделай еще одну характеристику. И плевать что и чип и девайс один и тот-же.
Но мы то ведь похоже так не можем ибо:
JavaScript:
this.log('Service found, getting characteristic...');
return service.getCharacteristic(0xffe1);

Постоянное применение loop() тут и сказывается. Если вы так обречены на loop(), то стройте Машину Тьюринга.
loop (впрочем как и setup) вызывается из недр обработчика. characteristic.addEventListener('characteristicvaluechanged', this);
То есть это просто название, сам код ваш, он просто по другому отформатирован :)
Вот так выглядит этот обработчик (если что блоки кода свернуты, чтобы все влезло в один скриншот, номера строк ble.js слева)
sshot-1.png
По приходу нового "characteristic value", библиотека сначала проверяет его на соответствие известных ей комбинаций idx и ds. Тех комбинаций, что отвечают за ошибки, инициализацию I2c шины, настройку ее скорости etc. Стандартные запросы она обрабатывает самостоятельно, для обработки ошибок вызывает сначал disconnect, а затем onerror. По приходу _ответа_ корректной инициализации i2с шины (idx==0x01, ds>=38) вызывает onsetup , во всех остальных случаях вызывается onloop. Правильнее было бы наверно вызывать его только при idx==0x0C, а все остальное отправлять в обработчик а-ла onUnknownCmd() это так только сейчас в голову пришло...

ps: Упреждая ваш вопрос, если вам не хватает доступа к какому-либо фрагменту событий, никто не мешает в любом нужном вам месте обработчика воткнуть вызов некоего гипотетического if(onMyEvent) onMyEvent(event); прописать его в конструкторе Ble
JavaScript:
 constructor(params) {
    this._log = params.log;
    this.onloop = params.onloop;
    this.onerror = params.onerror;
    this.onsetup = params.onsetup;
    this.onMyEvent = params.onMyEvent;
  }
и обработать его во внешнем коде.
JavaScript:
 ble = new Ble({log,
      onMyEvent: (event) =>{},
      onerror: (e,info)=>{ ...
 

pvvx

Активный участник сообщества
Дык и я о чем. Если я все правильно понял, await-async нам никак не поможет определить адресата сообщения, так как ответ не дается немедленно как результат функции Write или Read. Он приходит "сильно потом", когда чип на шине обработает ваш запрос и сформирует ответное сообщение. Примерно такая-же фигня происходит в шлюзах платежных систем PayPal или Yandex. Тамошние ребята борются с ней формируя ID трансакции, возвращая его при вызове функции "Write" и затем включая его в тело "отложенного" ответа. Как я понял у нас в прошивке ничего подобного нет и поэтому определять адресата сообщения можно только косвенно по ds ибо idx(который всегда равен 0x0C).
Первое что не понятно мне - зачем вам туда-сюда гонять лишнюю информацию?
Где в UDP, WEB, AJAX, websocket, UART и т.д. какие-то номера транзакций?
В Modbus TCP они есть, но 90% детских писанин у именитых брендов даже это не поддерживают и не используют, т.к. не у них не работает посылка пачки запросов, а потом уже разгребание принятых.
Второе - await-async вам дает возможность послать пачку запросов, а потом уже разгребать принятые, а так-же всякие events...
Push/Pop...
 

pvvx

Активный участник сообщества
Т.е. выходит, мы уперлись в java и как на там писать в javascript.
Может начнем с изучения алфавита? :)
Вы хотите написать API для прошивки, предназначенной для тестов возможностей данного чипа?
Не проще ли накатать немного строк в сам чип на СИ?
 
Первое что не понятно мне - зачем вам туда-сюда гонять лишнюю информацию?
Код:
<<  (11) ["0x09", "0x0c", "0xd0", "0x07", "0x03", "0x54", "0x09", "0x03", "0x10", "0x02", "0x21"]
<<  (6) ["0x04", "0x0c", "0xd0", "0x02", "0x19", "0xc0"]
Приходят два пакета, в одном 6 байт во втором 11
В одном дата-время, во втором температура. Как их различить иначе чем по размеру пакета?
Сейчас задействовал костыль, но криво же :) и таких ситуаций при реальном кодировании может быть вагон и маленькая тележка,

JavaScript:
 if (ds >= 9) {  // если пришло DateTime
 if (this.datetimeChanged) {
   ...
   this.datetimeChanged(new Date(year, month, date, hours, minutes, seconds, 0));
   }
   this.Read(e, new Uint8Array([0x11]), 2); // читаем температуру
} else {
   if (ds==4 && this.tempChanged) { // если пришла температура
   ..
   this.tempChanged(_msb + ((_lsb >> 6) * 0.25));
  }
  this.Read(e, new Uint8Array([0]), 7);  // спросить DateTime
}
(с) тут
делал отправку через .then так второй запрос вызывается в 4 раза реже первого...
а потом уже разгребать принятые,
Дык вопрос то как раз к разгребанию принятых, а не к отправке имеющихся :)
Вы хотите написать API для прошивки, предназначенной для тестов возможностей данного чипа?
Этой тестовой прошивки вполне хватит чтобы заменить в моем текущем проекте esp32. Вы же помните что я мастер прототипов? :)
Не проще ли накатать немного строк в сам чип на СИ?
Подправить саму прошивку под свои задачи мне было бы конечно интереснее. Но боюсь это будет куда сложнее и дольше отлаживать.
 

pvvx

Активный участник сообщества
Вы готовы показать пример минимальной прошивки выводящей время с ds3231 по BLE? :) Только это и ничего больше? То есть по сути запихнуть этот javascript в чип?
Не вижу никакого смысла в виде, как это делается для Arduino.
У BLE много вариантов применения и основная фича - для автономных устройств с малым потреблением. Это диктует разные подходы и режимы работы устройства.
Например режим выдачи рекламы, вариант MESH, режим соединения.
Для каждого случая необходимо жестко оптимизировать всю прошивку, включая и алгоритмы опроса датчика и кто и как будет принимать данные.
Но рынок ПО для IoT "умных домов" не развит в сфере BLE из-за кривого ПО в ESP32, а так-же отсутствия возможности создания простых программ у пользователя на смартфоны и прочие глупости.

Arduino не имеет концепции (ПО, примеров, устоявшихся алгоритмов, стиля написания, .... ) для поддержки автономных устройств с малым потреблением
 

pvvx

Активный участник сообщества
Вы готовы показать пример минимальной прошивки выводящей время с ds3231 по BLE? :) Только это и ничего больше?
Нет не готов за просто так.
Да и для Arduino у меня ценник от сотен тысч.руб. Всё остальное, с чем Arduino не связано - бесплатно.
То есть по сути запихнуть этот javascript в чип?
Т.е. сами не справились с javascript?
----
А так пойдет?
C:
#include "ble.h"
#include "ds3231.h"

u32 time_tisk;
t_ds3231_clock dclk;

void user_init() {
    ds3231_init();
}

void main_loop() {
    u32 tt = clock_time();
    if (tt - time_tisk >= 333 * CLOCK_SYS_CLOCK_1MS) {
        time_tisk = tt;
        if (SppDataCCC // Notify on?
                && (blc_ll_getCurrentState() & BLS_LINK_STATE_CONN) // Connect?
                && ds3231_read(&dclk, 0, sizeof(dclk))) { // DS3231 Read?
            bls_att_pushNotifyData(SPP_Server2Client_INPUT_DP_H, (u8 *) &dclk, sizeof(dclk));
        }
    }
}

int onSppReceiveData(void *par) {
    rf_packet_att_data_t *pp = par;
    u8 len = pp->l2cap - 3;
    if (len && len <= sizeof(SppDataBuffer)) {
        ds3231_write(pp->dat, 0, len);
    }
    return 0;
}

int onSppSendData(void *par) {
    (void) par; // rf_packet_att_write_t * p
    ds3231_read(SppDataBuffer, 0, sizeof(SppDataBuffer));
    return 0;
}
Наиболее приближенное к Arduino. Никаких экономий энергии и т.д.
И либа DS3231
C:
#ifndef DS3231_H_
#define DS3231_H_

#include "i2cbus.h"

#define DEV_I2C_ADDR (0x68<<1)

typedef struct { // clock
    u8 Seconds;
    u8 Minutes;
    u8 Hour;
    u8 Day;
    u8 Date;
    u8 Month;
    u8 Year;
} t_ds3231_clock;

inline int ds3231_write(u8 * buf, u8 reg_addr, u8 len) {
    return I2C_Write_A8(DEV_I2C_ADDR, reg_addr, buf, len);
}

inline int ds3231_init() {
    I2CBusInit(400);
    return I2CBusWriteWord(DEV_I2C_ADDR, 0x0e, 0x0088);
}

inline int ds3231_read(void * buf, u8 reg_addr, u8 len) {
    return I2C_Read_A8(DEV_I2C_ADDR, reg_addr, buf, len, 1);
}

#endif /* DS3231_H_ */
:)

Весь проект:
 

Вложения

pvvx

Активный участник сообщества
Если включить "Notify" у UUID FFE1, то передает первые 7 регистров (время-дата):
1615440143957.png

Если читать FFE1 - читает все регистры часов:
1615440330903.png
Если писать в FFE1 - пишет регистры часов с 0 по размеру отосланного блока.
---
И лог:
1615440349738.png
 
Не вижу никакого смысла в виде, как это делается для Arduino.
...
Да и для Arduino у меня ценник от сотен тысч.руб.
Далось вам это ардуино :) Совершенно не важно будет ли этот код похож на ардуиновский, важно чтобы он был легко читаем и легко понятен новичку.

Т.е. сами не справились с javascript?
Справился конечно, но криво на мой взгляд, о чем и отписал. вот сюда пока выложил Единственно надо будет даташит ds3231 покурить, что то больно редко температура в регистре обновляется. Ну и разогнать скорость обмена повыше :) а то секунды медленно переключаются(вы мне вроде пример уже давали). Ну и подрихтовать код от глупостей типа onerror(bla-bla) без предварительного if(onerror)

Ну и конечно сильно не хватает в вашей прошивке возможности подключится к прерыванию(-ям) датчика с выводом сообщения в ble при его срабатывании. Например у того же ds3231 есть будильники, по срабатыванию которых выставляется в "1" вывод INT на чипе. Хотелось бы припаять его куда нибудь к TLS и по появлению нужного сигнала выплевывать Notify.


Шикарно! Сейчас я до обеда убегаю в город по делам, а как приеду докопаюсь до вас с вопросом о минимальном наборе софта, необходимого для компиляции и запуска этого кода на модуле. Командно-строчного компилятора у них к слову нет? Ну чтобы можно было к любимому редактору пользователя прикрутить? в моем случае е Visual Studio Code.

pps: Удалось запустить программатор через USB-COM. Спаял все на соплях без dupon проводков с разъемчиками(чем дальше тем больше их ненавижу). Все получилось!
Готовый проверенный комплект программатора выложил сюда. Использовал вот такой USB-COM на cp2102 с выкусанными светодиодами.

usbcom_cp2102.jpgPrgJDY10.jpgmyprg.jpg
 
Сверху Снизу