Через Дуню лучше ничего не запитывать, А 12 вольт на VCC - смерть для Дуни. На RAW надо подавать питание, да и 12 вольт много для неё. Я приблизительно накидал схему питания у себя, там всем рулить будет отдельный МК.
6 вольт это шина для МК, большинство МК у меня будут работать на 3.3 вольт. Так что даже 9 вольт уже много, не говоря про 12. Тем более что на Pro Mini ставят маломощные стабилизаторы. И какие там будут амперы.
ModBus мне чего-то не покатил, решить строить свой велосипед...
наваял простой класс для передачи по RS485 пакетов, немного похоже на многие протоколы сразу, но как мне кажется в понимании будет попроще, имеет всего одну процедуру send и одну подключаемую процедуру на получение валидного пакета.
полевые испытания показали некоторые проблемы в классе доработал немного, добавил маркер начала кадра и перевел контрольные биты с XOR на XOR+циклический сдвиг, стало вполне надежно...
да скорость можно разгонять значительно выше, но классически рекомендумая - 19200, а для длинных линий (например 1 км) еще ниже, это связано с физическими своствами кабеля...
да скорость можно разгонять значительно выше, но классически рекомендумая - 19200, а для длинных линий (например 1 км) еще ниже, это связано с физическими своствами кабеля...
IMHO - на ваших расстояниях (домашне-огородных) скорость 485 интерфейса 115200 - вполне нормальна, причем с запасом.
При лучшем кабеле можно было бы сделать и мегабит. Но это уже вряд ли стоит.
да скорость можно разгонять значительно выше, но классически рекомендумая - 19200, а для длинных линий (например 1 км) еще ниже, это связано с физическими своствами кабеля...
IMHO - на ваших расстояниях (домашне-огородных) скорость 485 интерфейса 115200 - вполне нормальна, причем с запасом.
При лучшем кабеле можно было бы сделать и мегабит. Но это уже вряд ли стоит.
Да просто попробуйте, взяв бухту кабеля...
обязательно попробую, но позже... сейчас кроме кабеля для меня имеет значение скорость обработки даных на ардуинке, если скорость передачи будет выше чем скорость обработки - будут потери, по этому пока не будет в более менее полной мере понятны обьемы несчастя, скорость выбрать лучше меньше чем больше а потом пробовать ее поднимать....
кроме того сейчас UART работает паралеьно и на rs485 и на USB к компу (в режиме эмуляции COM), то есть еще тут могут быть заморочки....
кстати попутный вопрос:
использую "альтернативный" монитор порта https://sites.google.com/site/terminalbpp/ вроде все устраивает кроме одного, при подключении ардуинка уходит в ребут, а мне очень хочется подключатся "на живую" без ребута ардуинки...
использую "альтернативный" монитор порта https://sites.google.com/site/terminalbpp/ вроде все устраивает кроме одного, при подключении ардуинка уходит в ребут, а мне очень хочется подключатся "на живую" без ребута ардуинки...
Читал где-то, что это из-за линии DTR, которая на дуинах соединена с reset для удобства загрузки скетчей. Вариантов, помнится, предлагалось несколько: не юзать DTR, перерезать дорожку на плате, ещё чего-то там.
привет, интересная концепция, думаю что не новая но интересная. Тоже умный дом мастерю, пока есть только управление светом... как то у вас обсуждение зачахло. Если что нибудь наработается готов присоединиться к разработке, в силу своих скудных познаний)))
сейчас пишу свой протокол и библиотеки поддержки шины, я тут выкладывал промежуточный вариант но уже далеко ушел, получается очень даже интересно.
Протокол ориентирован на системы с малым ОЗУ, один пакет максимум 8 байт заголовок и до 24 байт данных, в заголовок встроено 4 битовых флага, одна команда может содержать до 8 пакетов данных (192 байта), всего команд - 16 из которых часть (наверно 10) будет свободно для пользовательского использования.
Шина будет уметь
1. находить и устранять конфликты ID
2. выбирать максимальную скорость обмена
3. планирую несколько ID зарезервировать на управление с компа и дебугер, по этому шина будет поддерживать слейвы с 1 по 240 ID.
из железа почти собрал один модуль, он будет мастером и в нем будет встроено SD хранилище + часы реального времени... не выкладываю все это по причине недоделок... осталось кнопки навесить и светодиоды.
то есть получается если мастер ек, все слейвы переходят в режим автономности и не обмениваются данными...
я тут думал над такой концепцией. если слейвы шлют в шину свои данные(на пример с датчиков температуры или движения), но не постоянно а только когда происходят значительные изменения (изменения которые достаточны для того, чтобы что то сделать, на пример изменение температуры на 3 градуса, или наличие движения, или резко запахло газом...) то есть в большинстве своем шина простаивает в ожидании важных команд. Когда слейв крикнул что то другие его слушают, и если информация им нужна, запоминают ее. (на пример контроллер освещения получил информацию об освещенности и движении и в зависимости от времени и режима работы (автоуправление по движению, охрана) принял решение о включении такой-то лампы). Другие же слейвы не связанные с освещенностью (на пример обогрев) эту информацию игнорируют и не учитывают в своей работе. а мастер ее принимает и пишет в лог.
При этом если слейв, которому нужна какая либо информация может получить ее как из лога мастера, так от слейва, если поймал её. Получается что система будет вполне жизнеспособна в плане связи между слейвами, даже если мастер молчит. Как то так...
большинстве своем шина простаивает в ожидании важных команд. Когда слейв крикнул что то другие его слушают, и если информация им нужна, запоминают ее.
При этом если слейв, которому нужна какая либо информация может получить ее как из лога мастера, так от слейва, если поймал её. Получается что система будет вполне жизнеспособна в плане связи между слейвами, даже если мастер молчит. Как то так...
такую систему нельзя реализовать на простых железках типа RS485, такое реально реализуемо только на буферизируемых устройствах (например HUB Ethernet), это уже и сложнее и дороже...
большинстве своем шина простаивает в ожидании важных команд. Когда слейв крикнул что то другие его слушают, и если информация им нужна, запоминают ее.
При этом если слейв, которому нужна какая либо информация может получить ее как из лога мастера, так от слейва, если поймал её. Получается что система будет вполне жизнеспособна в плане связи между слейвами, даже если мастер молчит. Как то так...
такую систему нельзя реализовать на простых железках типа RS485, такое реально реализуемо только на буферизируемых устройствах (например HUB Ethernet), это уже и сложнее и дороже...
RS-485 всего лишь физический уровень, причём, допускающий подключения нескольких устройств параллельно - шина. На нём возможно реализовать любой протол верхнего уровня, в т.ч. и токентинг, и несколько мастеров, и конкурентную среду. Например, модбас допускает несколько мастеров.
RS-485 всего лишь физический уровень, причём, допускающий подключения нескольких устройств параллельно - шина. На нём возможно реализовать любой протол верхнего уровня, в т.ч. и токентинг, и несколько мастеров, и конкурентную среду. Например, модбас допускает несколько мастеров.
ModBus - не допускает одновременно несколько мастеров, если мы говорим о нем как о транспорте нулевого уровня. Он допускает цикличного передача мастера, но мастер всегда в сети один на один момент, если это условие не соблюдать -будет бред в шине... банально две команды от двух мастеров сложатся на физическом уровне...
Зациклились просто с мастерами. Я уже выше давал ссылку на пример с режимом мульти мастер. Только зачем дома нужен космический корабль? Если у меня дома что то потечёт, то контроллер в ванной сразу перекроет воду. А через сколько об этом узнает мастер, через 1 или 2 секунды, какая разница. Что от этого изменится?
RS-485 всего лишь физический уровень, причём, допускающий подключения нескольких устройств параллельно - шина. На нём возможно реализовать любой протол верхнего уровня, в т.ч. и токентинг, и несколько мастеров, и конкурентную среду. Например, модбас допускает несколько мастеров.
ModBus - не допускает одновременно несколько мастеров, если мы говорим о нем как о транспорте нулевого уровня. Он допускает цикличного передача мастера, но мастер всегда в сети один на один момент, если это условие не соблюдать -будет бред в шине... банально две команды от двух мастеров сложатся на физическом уровне...
Я разве где-то говорил об одновременной работе двух мастеров в ModBus?
на праздники начал писать отладчик (снифер+дебагер), начал с того, что написал формы разбора произвольного пакета и сборки пакета по параметрам, сейчас прикручиваю работу с COM портом.
короче получается вполне себе система, возможно и для комерческого использования пойдет, вот теперь думаю - выкладывать в нахаляву или нет :)
ну че разобрались с интерфейсом?? хотел сделать мнемо схему состояния и контроля устройств по интерфейсу вот с интерфейсом возникли проблеммы, если кому интересно пишите почту скину саму мнемо схему,
почти собрал модуль мастера (состоит NANO+часы+SD), осталось поставить диоды на питание (которых пока нет) + 3 кнопки и 5 светодиодов, и лицевую панель, сейчас все собрано в корпусе и работает, но пока без кнопок...
сделал генератор трафика (на NANO)
практически доделал по софту:
1. классы для ардуино и общий шаблон скеча для мастера и слейвов
2. анализатор трафика на дельфи (с разбором пакетов, фильтрами, показателями мусорности и т.д.)
3. отладчик на дельфи (позволяет выделять обмен между мастером и одним слейвом, при этом умеет заставлять мастера посылать в степовом режиме, при этом вся остальная шина работает в штатном режиме)
4. отдельный канал для отладочных сообщений (приходят только отладчику)
осталось реализовать:
1. автоматическая смена ID слейвом
2. обработку широковещательных пакетов
после этого можно будет приступать к слейвам, первый слейв будет: NANO+давление+шина с датчиками температуры (улица и дома по зонам), тут у меня все есть и даже скетч я писал перед новым годом, останется его адаптировать. Следующий этап будет - это экранчик...
Тут в начале говорилось об обмене модулями. Сам начал с освещения. Модуль сделал на печатке. Могу выложить инфу по модулю света, если интересно, хотя сам ниразу не программист, поэтому при вашем уровне, вам врядли понравится.
дошел до состояния когда нужно отлаживать одновременно несколько устройств, есть вопросы
1. можно- ли подключить несколько NANO по USB к одному компу на разные COM порты ? что для этого надо? и как их различать?
2. стоит-ли делать отдельный макетный модуль (NANO+RS485) который использовать как эталонный ответчик мастеру / генератор помех / и т.д. или использовать реальные устройства?
1. можно- ли подключить несколько NANO по USB к одному компу на разные COM порты ? что для этого надо? и как их различать?
Логичней и комп подключать через 485
комп нужен не только для монитора данных но и для загрузки исправленых программ, при этом будет удобно ничего не перетыкивать а иметь открытым сразу 2 проекта и перезаливать их по мере необходимости....
Я юзаю второй подход, читабельность кода от этого не страдает, если он правильно отформатирован табуляциями, разнесён на функциональные блоки, оформлен комментариями.
3. слейв с датчиками температуры даллас на он вире
данные датчиков темппературы и часов корректно отображаются на дисплеи, после разрыва связи все само востанавливается... конфликты одного ID в сети разруливаются... количество модулей с дисплеями ограничено лишь разумным пределом.
Сейчас занимаюсь переписыванием кода на более универсальный с прицелом на расширение, жду тачскринов (буду переделывать на тачи) и еще мелочевки. Так что лично для меня вопрос протокола - не актуален уже, то что сейчас работает я пока не пробовал на больших расстояниях, но на 10 метровом кабеле вообще никаких проблемм (уже 3суток работают на стенде и комп ловит "мусор", пока ни одного ошибочного байта)
зы
выкладывать правда некуда, файлов реально много будет, нужен репозитарий како-то более менее надежный...
меня интересует как вывести все на мнемосхему с помощью web, ethernet шилда, с помощью Ajax неплохой внешний вид получится!!!! скиньте пожалуйста свой проект очень интересно!!! у меня в проекте управление скважины (Насос включение выключение по давлению, подогрев трубы в мороз, состояния температуры в скважине и самой воды,), уличный датчик (Температура, влажность), и септик (Температура, состояние, аварийное переполнение) вот думал сделать на RS485 и поставить мегу 2560 как мастера с Ethernet шилдом с смартфона или с компьютера открываем страничку и смотрим что там происходит (мнемосхема), или задаем параметры включения и выключения например подогрева трубы в мороз!! вот ищу подоные проекты что бы осущиствить свои планы, у вас помойму есть чему поучится скиньте пожалуйста скетч на почту Anton_Kos87@mail.ru
выложу наброски через неделю, другую сейчас очень сыро еще...
в качестве затравки кину 2 модуля сюда (они пока еще не полностью переписаны на новую идеологию, но все более менее понятно), что-бы было понятно в каком оформление это будет дальше, может кто покритикует...
// Общие переменные не зависящие от конфигурации устройства
058
BusRS485 Bus(0, 5, 4); // создание шины 485, порт - 0, пин - 5 (Read), пин - 4 (Write), можно назначить на один пин, но не будет работать полное отключение шины
059
unsigned longtime_loop_new = 0; // время начала основного цикла, на это время идет синхронизация всех команд и пауз
060
uint8_t Status = 0; // битовый статус контроллера, включая затребованые команды
выложу наброски через неделю, другую сейчас очень сыро еще...
в качестве затравки кину 2 модуля сюда (они пока еще не полностью переписаны на новую идеологию, но все более менее понятно), что-бы было понятно в каком оформление это будет дальше, может кто покритикует...
похоже автор статьи забыл выложить вкусняшку и пропал
Долго и внимательно читал тему, но так и не осознал ответа на свой вопрос, как бороться на rs485 с коллизиями? Уважаемый vde69 как ТС и остальная общественность, не могли бы Вы дать комментарий по следующим вопросам
1. Я наивно полагал, что если один что-то передает, то у других Serial.available>0 что говорит о том, что линия занята и надо просто дождаться ее освобождения
но на практике столкнулся с тем, что когда два устройства начинают что-то вещать у меня не просто на шине каша, у меня они перестают вообще что-либо слать.
2. Так можно или нет реализовать схему с мультимастером, и как тогда бороться с коллизиями.
Объясню свою позицию: я согласен с звучавшими в теме мнениями, что вариант запрос-ответ не совсем здорово, потому как нажав кнопку я хочу увидеть реакцию в течении одной секунды а не ждать пока Мастер опросит "мое" устройство. То есть, я нажимаю кнопку, МК посылает данные на Мастер, получает команду и выполняет ее.
В интеренте есть описания протоколов типа KNX, SmartBus но я не нашел готовых реализаций.
В общем темой живо интересуюсь, есть готовые наработки, но при усложнении системы возникли непонятки с коллизиями, и главный вопрос, помему при коллизии больше до МК не достучаться до его перезагрузки.
Я в самом начале писал про интервально-маркерный метод для разделения устройств в моноканале. И приводил ссылку на книгу с подробным описанием этого метода.
Так можно или нет реализовать схему с мультимастером, и как тогда бороться с коллизиями.
Маркерное кольцо и есть мультимастерная схема, только она в реализации несколько сложнее. RS485 не позволяет определить наличие колизии, для этого используют CAN.
gonzales пишет:
я согласен с звучавшими в теме мнениями, что вариант запрос-ответ не совсем здорово, потому как нажав кнопку я хочу увидеть реакцию в течении одной секунды а не ждать пока Мастер опросит "мое" устройство. То есть, я нажимаю кнопку, МК посылает данные на Мастер, получает команду и выполняет ее.
Даже в маркерном кольце устройство должно дождаться, пока ему не передадут маркер, и только после этого оно становится мастером на шине. Что мешает в схеме мастер-слэйв опрашивать подчиненные устройства чаще, чем раз в 1 сек?
gonzales пишет:
и главный вопрос, помему при коллизии больше до МК не достучаться до его перезагрузки.
Маркерное кольцо - это не мультимастер, по сути это тот же мастер-слейв, только вместо запроса от мастера я гоняю по сети маркер. Или я не прав?
>Что мешает в схеме мастер-слэйв опрашивать подчиненные устройства чаще, чем раз в 1 сек?
по моим подсчетам в сети будет около 100 устройств. Пакет у меня 10 байтный. При скорости 19200 это получается с небольшим запасом 0.01с на запрос-ответ. То есть мастер только и будет занят тем, что опрашивать устройства. Как только на это наложатся временные лаги на выполнение команд самим мастером будет просадка по времени отклика.
>RS485 не позволяет определить наличие колизии, для этого используют CAN
уже читаю про CAN, правда с реализациями на ардуино под can достаточно туго.
>Кривая реализация протокола обмена.
А что тут может быть кривого? Не сочтите за труд посмотрите скеч
Маркерное кольцо в каждый конкретный промежуток времени это мастер-слейв. Мастером может быть любое устройство, которому передан маркер - следовательно это мультимастерная схема.
gonzales пишет:
по моим подсчетам в сети будет около 100 устройств.
Рискну предположить, интерфейс реализован на MAX485, а он позволяет только 32 устройства на шине.
gonzales пишет:
А что тут может быть кривого?
Строка 51. Что если пришло не 9 символов, а меньше или больше?
Через Дуню лучше ничего не запитывать, А 12 вольт на VCC - смерть для Дуни. На RAW надо подавать питание, да и 12 вольт много для неё. Я приблизительно накидал схему питания у себя, там всем рулить будет отдельный МК.
я перепутал, разумеется 12 вольт на VIN а не на VCC... можно не 12 а 10, но блок питания на 12 проще найти...
6 вольт на шину - явно мало, во первых ампер потребуется много (и следовательно кабель толстый), во вторых будут просадки на длине..
Раздавать 9в (их на Vin дуни) а где нужны силовые 5в (модуль реле или еще что) ставить дс дс по месту типа такого http://ru.aliexpress.com/item/1pc-Small-LM2596-power-supply-module-DC-DC-BUCK-3A-adjustable-buck-module-regulator-ultra-LM2596S/32582253900.html?spm=2114.03010208.3.148.kCRENE&ws_ab_test=searchweb201556_0,searchweb201602_1_10017_10005_10006_10034_10021_507_10022_508_10020_10018_10019,searchweb201603_6&btsid=3f77c8cc-4faf-4c17-b0d0-b57d85764283
6 вольт это шина для МК, большинство МК у меня будут работать на 3.3 вольт. Так что даже 9 вольт уже много, не говоря про 12. Тем более что на Pro Mini ставят маломощные стабилизаторы. И какие там будут амперы.
ModBus мне чего-то не покатил, решить строить свой велосипед...
наваял простой класс для передачи по RS485 пакетов, немного похоже на многие протоколы сразу, но как мне кажется в понимании будет попроще, имеет всего одну процедуру send и одну подключаемую процедуру на получение валидного пакета.
Критика приветствуется :)
01
#include <busrs485.h>
02
03
04
BusRS485 Bus(0, 0, 2);
// this is master and RS-232 or USB-FTDI
05
06
void
setup
() {
07
Bus.begin( 19200 );
// baud-rate at 19200
08
Bus.attachInPackInterrupt(BusInPack);
// подключим процедуру для обработки полученых пакетов
09
}
10
11
void
loop
() {
12
Bus.poll();
// вызов процедур чтения порта
13
}
14
15
// процедура вызывается при получении валидного пакета
16
void
BusInPack (){
17
// тут делаем, что нам надо при получении пакета
18
// например перешлем его другому адресату
19
if
(Bus.Id_Destination() == 0) {
20
if
(Bus.send(22,
// ID получателя
21
Bus.Type(),
// тип пакета
22
Bus.Func(),
// номер функции
23
Bus.SizeDate(),
// размер дополнительных данных
24
Bus.Date()))
// указатель на буфер дополнительных данных
25
{;}
// удачно отпрвили
26
else
{;}
// ошибка при отправки
27
}
28
29
}
а это BusRS485.h
001
">
002
003
/* !!!!!!!!!!!!!!!!!!!!!!!!!! ВНИМАНИЕ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
004
использует расширение команды Serial.peek(), для этого нужно добавить
005
006
в HardwareSerial.h
007
-----------------------------------------------------------
008
virtual int peek(uint8_t); // надстройка для BusRS485
009
-----------------------------------------------------------
010
011
в HardwareSerial.cpp
012
-----------------------------------------------------------
013
int HardwareSerial::peek(uint8_t c) // надстройка для BusRS485
014
{
015
int i = this->available();
016
// проверяем, что индекс в рабочем диапазоне
017
if (c < i) {
018
return _rx_buffer->buffer[(SERIAL_BUFFER_SIZE + c + _rx_buffer->tail) % SERIAL_BUFFER_SIZE];
019
} else {
020
return -1;
021
}
022
}
023
-----------------------------------------------------------
024
*/
025
026
027
#include <inttypes.h>
028
#include <arduino.h>
029
#include <print.h>
030
#include <wiring_private.h>
031
032
033
// буфер Serial может хранить 64 байта или 16, заголовок пакета 7 байт, по этому размер буфера дополнительных данных
034
// 57 или 9 байт, такой малый размер буфера надо учитывать !!!
035
// данная библиотека не будет работать с малым размером буфера для шины RS485, пин управления будет отключен !!!
036
#if (RAMEND < 1000)
037
#define MAX_BUFFER 9
038
#else
039
#define MAX_BUFFER 57
040
#endif
041
042
// описание пакета, данные передаются и принимаются именно в такой последовательности
043
typedef
struct
{
044
uint8_t Id_Destination;
// ID получателя пакета
045
uint8_t Id_Source;
// ID отправителя пакета
046
uint8_t Type;
// Тип пакета
047
uint8_t Func;
// Номер команды определяет тип дополнительных данных и правила их обработки
048
uint8_t SizeDate;
// Размер дополнительных данных пакета
049
uint8_t xor_Data;
// xor (контроль четности) дополнительных данных пакета
050
uint8_t xor_Head;
// xor (контроль четности) заголовка, служит для определения корректности заголовка и выделения кадра из потока данных
051
uint8_t Data[MAX_BUFFER];
// дополнительные данные
052
}
053
BusRS485_Pack;
054
055
volatile
static
voidFuncPtr _InPack;
056
057
class
BusRS485 {
058
private
:
059
HardwareSerial *port;
// указатель на Serial class
060
uint8_t Id;
// 0=master, 1..246=slave, 247=резерв
061
uint8_t ControlPin;
// номер пина контроля RS-485
062
BusRS485_Pack InPack;
// входящий пакет
063
uint8_t FInPack;
// флаг входящего пакета, 0-ждем заголовок, 1-ждем дополнительные данные, 2-пакет загружен валиден, 3-пакет загружен, но не валиден
064
065
void
init(uint8_t Id, uint8_t PortNum, uint8_t ControlPin);
066
067
public
:
068
BusRS485(uint8_t Id, uint8_t PortNum, uint8_t ControlPin);
069
070
uint8_t Id_Destination (
void
);
071
uint8_t Id_Source (
void
);
072
uint8_t Type (
void
);
073
uint8_t Func (
void
);
074
uint8_t SizeDate (
void
);
075
uint8_t *Date (
void
);
076
077
void
begin(
long
Speed);
078
void
attachInPackInterrupt(
void
(*)());
079
void
poll();
// процедура запуска обработки получения данных
080
void
on();
// включение шина + COM порт
081
void
off();
// выключение шины (только COM порт)
082
083
// возвращает true если данные отправлены, и false если ошибка
084
boolean send(int8_t,
// ID получателя
085
int8_t,
// тип пакета
086
int8_t,
// номер функции
087
int8_t,
// размер дополнительных данных
088
uint8_t *);
// указатель на буфер дополнительных данных
089
};
090
091
/* _____PUBLIC FUNCTIONS_____________________________________________________ */
092
093
BusRS485::BusRS485(uint8_t Id, uint8_t PortNum, uint8_t ControlPin) {
094
init(Id, PortNum, ControlPin);
095
}
096
097
098
uint8_t BusRS485::Id_Destination () {
099
return
InPack.Id_Destination;
100
}
101
102
uint8_t BusRS485::Id_Source (){
103
return
InPack.Id_Source;
104
}
105
106
uint8_t BusRS485::Type (){
107
return
InPack.Type;
108
}
109
110
uint8_t BusRS485::Func (){
111
return
InPack.Func;
112
}
113
114
uint8_t BusRS485::SizeDate (){
115
return
InPack.SizeDate;
116
}
117
118
uint8_t * BusRS485::Date (){
119
return
&(InPack.Data[0]);
120
}
121
122
123
boolean BusRS485::send(int8_t Id,
// ID получателя
124
int8_t Type,
// тип пакета
125
int8_t Func,
// номер функции
126
int8_t SizeDate,
// размер дополнительных данных
127
uint8_t *Data) {
// указатель на буфер дополнительных данных
128
129
if
(MAX_BUFFER >= SizeDate) {
// посылаем только если доп данные влезут в буфер
130
131
uint8_t xor_Data = 0;
// xor (контроль четности) дополнительных данных пакета
132
uint8_t xor_Head = 0;
// xor (контроль четности) заголовка, служит для определения корректности заголовка и выделения кадра из потока данных
133
134
for
(
int
i=0; i < SizeDate; i++){
135
xor_Data ^= port->peek(i);
136
}
137
138
xor_Head = Id
139
^
this
->Id
140
^Type
141
^Func
142
^SizeDate
143
^xor_Data;
144
145
port->write(Id);
146
port->write(
this
->Id);
147
port->write(Type);
148
port->write(Func);
149
port->write(SizeDate);
150
port->write(xor_Data);
151
port->write(xor_Head);
152
153
for
(
int
i=0; i < SizeDate; i++){
154
port->write(Data[i]);
155
}
156
return
true
;
157
}
158
else
{
159
return
false
;
160
}
161
}
162
163
void
BusRS485::begin(
long
Speed) {
164
port->begin(Speed);
165
port->flush();
166
FInPack = 0;
167
}
168
169
170
void
BusRS485::attachInPackInterrupt(
void
(*_userFunc)(
void
))
171
{
172
_InPack = _userFunc;
173
}
174
175
void
BusRS485::on() {
176
if
(ControlPin > 1) {
177
// с малым размера памяти устройства - оно выключено...
178
#if (RAMEND < 1000)
179
digitalWrite(ControlPin, LOW);
180
#else
181
digitalWrite(ControlPin, HIGH);
182
#endif
183
port->flush();
184
}
185
}
186
187
void
BusRS485::off() {
188
if
(ControlPin > 1) {
189
digitalWrite(ControlPin, LOW);
190
}
191
}
192
193
void
BusRS485::poll() {
194
uint8_t i = 0;
195
uint8_t i1 = 0;
196
int
ii;
197
boolean f =
true
;
198
199
while
(f) {
200
if
(FInPack > 3) {FInPack = 0;}
// не понятный флаг, значит будем ждать заголовок
201
else
if
(FInPack == 2) {
break
;}
// пакет загружен и не обработан
202
else
if
(FInPack == 3) {
break
;}
// пакет загружен и не валиден
203
204
ii = port->available();
205
206
// 0-ждем заголовок, 1-ждем дополнительные данные
207
if
(FInPack == 0 && ii < 7) {
208
break
;
// ждем заголовка, но данных мало
209
}
210
211
if
(ii >= 7) {
// возможно это заголовок пакета, посчитаем контрольный символ
212
i == ( port->peek(0)
213
^ port->peek(1)
214
^ port->peek(2)
215
^ port->peek(3)
216
^ port->peek(4)
217
^ port->peek(5));
218
if
(i == port->peek(6)) {
219
// да это заголовок пакета,
220
221
// проверим предыдущий заголовок
222
if
(FInPack == 1) {
223
// !!!! ошибочный пакет, пока просто выбросим !!!!
224
FInPack = 0;
225
}
226
227
// загрузим пакет
228
InPack.Id_Destination = port->read();
229
InPack.Id_Source = port->read();
230
InPack.Type = port->read();
231
InPack.Func = port->read();
232
InPack.SizeDate = port->read();
233
InPack.xor_Data = port->read();
234
InPack.xor_Head = port->read();
235
236
if
(InPack.SizeDate == 0) {
237
// пакет не содержит дополнительных данных, только номер и команду
238
FInPack = 2;
239
break
;
240
}
241
else
{
242
// заголовок загружен, нужно пытатся загрузить данные
243
FInPack = 1;
244
ii = port->available();
245
continue
;
246
}
247
}
248
}
249
250
if
(FInPack == 0) {
251
// ждем заголовок, но его нет, значит есть мусор
252
port->read();
253
}
254
else
if
(ii < InPack.SizeDate) {
255
break
;
// ждем данных, но их мало
256
}
257
else
{
258
// Возможно это дополнительные данные
259
i == 0;
260
for
(
int
i1=0; i1 < InPack.SizeDate; i1++){
261
i ^= port->peek(i1);
262
}
263
if
(i == InPack.xor_Data) {
264
// это правильные дополнительные данные
265
for
(
int
i1=0; i1 < InPack.SizeDate; i1++){
266
InPack.Data[i1] == port->read();
267
}
268
FInPack = 2;
269
break
;
270
}
271
else
{
272
// контроль доп данных не сошелся.... идем дальше
273
port->read();
274
}
275
}
276
}
277
278
if
(FInPack == 2) {
279
// запускаем подключеную обработку
280
_InPack();
281
FInPack = 0;
282
}
283
if
(FInPack == 3) {
284
// !!!! ошибочный пакет, пока просто выбросим !!!!
285
FInPack = 0;
286
}
287
}
288
289
290
/* _____PRIVATE FUNCTIONS_____________________________________________________ */
291
292
293
// конструктор
294
void
BusRS485::init(uint8_t Id, uint8_t PortNum, uint8_t ControlPin) {
295
this
->Id = Id;
296
switch
( PortNum ) {
297
#if defined(UBRR1H)
298
case
1:
299
port = &Serial1;
300
break
;
301
#endif
302
303
#if defined(UBRR2H)
304
case
2:
305
port = &Serial2;
306
break
;
307
#endif
308
309
#if defined(UBRR3H)
310
case
3:
311
port = &Serial3;
312
break
;
313
#endif
314
case
0:
315
default
:
316
port = &
Serial
;
317
break
;
318
}
319
320
this
->ControlPin = ControlPin;
321
if
(ControlPin > 1) {
322
pinMode(ControlPin, OUTPUT);
323
}
324
this
->on();
325
}
Не вижу контрольной суммы посылки
Не вижу контрольной суммы посылки
есть 2 байта XOR ов, один на шапку и второй на доп данные...
полевые испытания показали некоторые проблемы в классе доработал немного, добавил маркер начала кадра и перевел контрольные биты с XOR на XOR+циклический сдвиг, стало вполне надежно...
а это BusRS485.h
001
/* !!!!!!!!!!!!!!!!!!!!!!!!!! ВНИМАНИЕ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
002
использует расширение команды Serial.peek(), для этого нужно добавить
003
004
в HardwareSerial.h
005
-----------------------------------------------------------
006
virtual int peek(uint8_t); // надстройка для BusRS485
007
-----------------------------------------------------------
008
009
в HardwareSerial.cpp
010
-----------------------------------------------------------
011
int HardwareSerial::peek(uint8_t c) // надстройка для BusRS485
012
{
013
int i = this->available();
014
// проверяем, что индекс в рабочем диапазоне
015
if (c < i) {
016
return _rx_buffer->buffer[(SERIAL_BUFFER_SIZE + c + _rx_buffer->tail) % SERIAL_BUFFER_SIZE];
017
} else {
018
return -1;
019
}
020
}
021
-----------------------------------------------------------
022
*/
023
024
025
#include
026
#include
027
#include
028
#include
029
030
031
// буфер Serial может хранить 64 байта или 16, заголовок пакета 7 байт, по этому размер буфера дополнительных данных
032
// 57 или 9 байт, такой малый размер буфера надо учитывать !!!
033
// Рекомендуемый размер дополнительных данных не более 24 байта (2 полных кадра в буфере)
034
// данная библиотека не будет работать с малым размером буфера для шины RS485, пин управления будет отключен !!!
035
#if (RAMEND < 1000)
036
#define MAX_BUFFER 8
037
#else
038
#define MAX_BUFFER 56
039
#endif
040
041
// описание пакета, данные передаются и принимаются именно в такой последовательности
042
typedef
struct
{
043
uint8_t Mark;
// маркер начала кадра (введен для стабильности распознования кадров), всегда $AA, 10101010h, 170
044
uint8_t Id_Destination;
// ID получателя пакета
045
uint8_t Id_Source;
// ID отправителя пакета
046
uint8_t Type;
// Тип пакета
047
uint8_t Func;
// Номер команды определяет тип дополнительных данных и правила их обработки
048
uint8_t SizeDate;
// Размер дополнительных данных пакета
049
uint8_t CRC_Data;
// CRC дополнительных данных пакета
050
uint8_t CRC_Head;
// CRC заголовка, служит для определения корректности заголовка и выделения кадра из потока данных
051
uint8_t Data[MAX_BUFFER];
// дополнительные данные, рекомендуемый размер не более 24 байт, максимальный размер 56 байт
052
}
053
BusRS485_Pack;
054
055
volatile
static
voidFuncPtr _InPack;
056
057
class
BusRS485 {
058
private
:
059
HardwareSerial *port;
// указатель на Serial class
060
uint8_t Id;
// 0=master, 1..246=slave, 247=резерв
061
uint8_t ControlPin;
// номер пина контроля RS-485
062
BusRS485_Pack InPack;
// входящий пакет
063
uint8_t FInPack;
// флаг входящего пакета, 0-ждем заголовок, 1-ждем дополнительные данные, 2-пакет загружен валиден, 3-пакет загружен, но не валиден
064
065
void
init(uint8_t, uint8_t, uint8_t);
066
uint8_t CRC(uint8_t, uint8_t);
067
068
public
:
069
BusRS485(uint8_t Id, uint8_t PortNum, uint8_t ControlPin);
070
071
uint8_t Id_Destination (
void
);
072
uint8_t Id_Source (
void
);
073
uint8_t Type (
void
);
074
uint8_t Func (
void
);
075
uint8_t SizeDate (
void
);
076
uint8_t *Date (
void
);
077
078
void
begin(
long
Speed);
079
void
attachInPackInterrupt(
void
(*)());
080
void
poll();
// процедура запуска обработки получения данных
081
void
on();
// включение шина + COM порт
082
void
off();
// выключение шины (только COM порт)
083
084
// возвращает true если данные отправлены, и false если ошибка
085
boolean send(int8_t,
// ID получателя
086
int8_t,
// тип пакета
087
int8_t,
// номер функции
088
int8_t,
// размер дополнительных данных
089
uint8_t *);
// указатель на буфер дополнительных данных
090
};
091
092
/* _____PUBLIC FUNCTIONS_____________________________________________________ */
093
094
BusRS485::BusRS485(uint8_t Id, uint8_t PortNum, uint8_t ControlPin) {
095
init(Id, PortNum, ControlPin);
096
}
097
098
099
uint8_t BusRS485::Id_Destination () {
100
return
InPack.Id_Destination;
101
}
102
103
uint8_t BusRS485::Id_Source (){
104
return
InPack.Id_Source;
105
}
106
107
uint8_t BusRS485::Type (){
108
return
InPack.Type;
109
}
110
111
uint8_t BusRS485::Func (){
112
return
InPack.Func;
113
}
114
115
uint8_t BusRS485::SizeDate (){
116
return
InPack.SizeDate;
117
}
118
119
uint8_t * BusRS485::Date (){
120
return
&(InPack.Data[0]);
121
}
122
123
124
boolean BusRS485::send(int8_t Id,
// ID получателя
125
int8_t Type,
// тип пакета
126
int8_t Func,
// номер функции
127
int8_t SizeDate,
// размер дополнительных данных
128
uint8_t *Data) {
// указатель на буфер дополнительных данных
129
130
if
(MAX_BUFFER >= SizeDate) {
// посылаем только если доп данные влезут в буфер
131
132
uint8_t CRC_Data = 0;
// CRC дополнительных данных пакета
133
uint8_t CRC_Head = 0;
// CRC заголовка, служит для определения корректности заголовка и выделения кадра из потока данных
134
135
for
(
int
i=0; i < SizeDate; i++){
136
CRC_Data = CRC(port->peek(i), CRC_Data);
137
}
138
139
CRC_Head = CRC(170, CRC_Head);
140
CRC_Head = CRC(Id, CRC_Head);
141
CRC_Head = CRC(
this
->Id, CRC_Head);
142
CRC_Head = CRC(Type, CRC_Head);
143
CRC_Head = CRC(Func, CRC_Head);
144
CRC_Head = CRC(SizeDate, CRC_Head);
145
CRC_Head = CRC(CRC_Data, CRC_Head);
146
147
148
port->write(170);
149
port->write(Id);
150
port->write(
this
->Id);
151
port->write(Type);
152
port->write(Func);
153
port->write(SizeDate);
154
port->write(CRC_Data);
155
port->write(CRC_Head);
156
157
for
(
int
i=0; i < SizeDate; i++){
158
port->write(Data[i]);
159
}
160
return
true
;
161
}
162
else
{
163
return
false
;
164
}
165
}
166
167
void
BusRS485::begin(
long
Speed) {
168
port->begin(Speed);
169
port->flush();
170
FInPack = 0;
171
}
172
173
174
void
BusRS485::attachInPackInterrupt(
void
(*_userFunc)(
void
))
175
{
176
_InPack = _userFunc;
177
}
178
179
void
BusRS485::on() {
180
if
(ControlPin > 1) {
181
// с малым размера памяти устройства - оно выключено...
182
#if (RAMEND < 1000)
183
digitalWrite(ControlPin, LOW);
184
#else
185
digitalWrite(ControlPin, HIGH);
186
#endif
187
port->flush();
188
}
189
}
190
191
void
BusRS485::off() {
192
if
(ControlPin > 1) {
193
digitalWrite(ControlPin, LOW);
194
}
195
}
196
197
void
BusRS485::poll() {
198
uint8_t mCRC = 0;
199
uint8_t i1 = 0;
200
int
ii;
201
boolean f =
true
;
202
203
while
(f) {
204
if
(FInPack > 3) {FInPack = 0;}
// не понятный флаг, значит будем ждать заголовок
205
else
if
(FInPack == 2) {
break
;}
// пакет загружен и не обработан
206
else
if
(FInPack == 3) {
break
;}
// пакет загружен и не валиден
207
208
ii = port->available();
209
210
// 0-ждем заголовок, 1-ждем дополнительные данные
211
if
(FInPack == 0 && ii < 8) {
212
break
;
// ждем заголовка, но данных мало
213
}
214
215
if
(ii >= 8 && port->peek(0) == 170) {
// возможно это заголовок пакета, посчитаем контрольный символ
216
mCRC = CRC(170, mCRC);
217
mCRC = CRC(port->peek(1), mCRC);
218
mCRC = CRC(port->peek(2), mCRC);
219
mCRC = CRC(port->peek(3), mCRC);
220
mCRC = CRC(port->peek(4), mCRC);
221
mCRC = CRC(port->peek(5), mCRC);
222
mCRC = CRC(port->peek(6), mCRC);
223
224
if
(mCRC == port->peek(7)) {
225
// да это заголовок пакета,
226
227
// проверим предыдущий заголовок
228
if
(FInPack == 1) {
229
// !!!! ошибочный пакет, пока просто выбросим !!!!
230
FInPack = 0;
231
}
232
233
// загрузим пакет
234
InPack.Mark = port->read();
235
InPack.Id_Destination = port->read();
236
InPack.Id_Source = port->read();
237
InPack.Type = port->read();
238
InPack.Func = port->read();
239
InPack.SizeDate = port->read();
240
InPack.CRC_Data = port->read();
241
InPack.CRC_Head = port->read();
242
243
if
(InPack.SizeDate == 0) {
244
// пакет не содержит дополнительных данных, только номер и команду
245
FInPack = 2;
246
break
;
247
}
248
else
{
249
// заголовок загружен, нужно пытатся загрузить данные
250
FInPack = 1;
251
ii = port->available();
252
continue
;
253
}
254
}
255
}
256
257
if
(FInPack == 0) {
258
// ждем заголовок, но его нет, значит есть мусор
259
port->read();
260
}
261
else
if
(ii < InPack.SizeDate) {
262
break
;
// ждем данных, но их мало
263
}
264
else
{
265
// Возможно это дополнительные данные
266
mCRC == 0;
267
for
(
int
i1=0; i1 < InPack.SizeDate; i1++){
268
mCRC = CRC(port->peek(i1), mCRC);
269
}
270
if
(mCRC == InPack.CRC_Data) {
271
// это правильные дополнительные данные
272
for
(
int
i1=0; i1 < InPack.SizeDate; i1++){
273
InPack.Data[i1] == port->read();
274
}
275
FInPack = 2;
276
break
;
277
}
278
else
{
279
// контроль доп данных не сошелся.... идем дальше
280
port->read();
281
}
282
}
283
}
284
285
if
(FInPack == 2) {
286
// запускаем подключеную обработку
287
_InPack();
288
FInPack = 0;
289
}
290
if
(FInPack == 3) {
291
// !!!! ошибочный пакет, пока просто выбросим !!!!
292
FInPack = 0;
293
}
294
}
295
296
297
/* _____PRIVATE FUNCTIONS_____________________________________________________ */
298
299
300
// конструктор
301
void
BusRS485::init(uint8_t Id, uint8_t PortNum, uint8_t ControlPin) {
302
this
->Id = Id;
303
switch
( PortNum ) {
304
#if defined(UBRR1H)
305
case
1:
306
port = &Serial1;
307
break
;
308
#endif
309
310
#if defined(UBRR2H)
311
case
2:
312
port = &Serial2;
313
break
;
314
#endif
315
316
#if defined(UBRR3H)
317
case
3:
318
port = &Serial3;
319
break
;
320
#endif
321
case
0:
322
default
:
323
port = &
Serial
;
324
break
;
325
}
326
327
this
->ControlPin = ControlPin;
328
if
(ControlPin > 1) {
329
pinMode(ControlPin, OUTPUT);
330
}
331
this
->on();
332
}
333
334
335
// функция делает XOR и после циклический сдвиг
336
uint8_t BusRS485::CRC(uint8_t a, uint8_t b) {
337
uint8_t c;
338
c = a ^ b;
339
return
c << 1 | c >> 7;
340
}
341
342
343
344
}
скорость допустим 9600 бит в сек
А если допустить скорость хотя бы 115200? Зачем на 9600-то, если интерфейс позволяет гораздо большую скорость?
скорость допустим 9600 бит в сек
А если допустить скорость хотя бы 115200? Зачем на 9600-то, если интерфейс позволяет гораздо большую скорость?
скорость определяется двумя фактами
1. физическими характеристиками трассы (длинна, волновое сопротивление, затухание, помехи и т.д.)
2. используемыми интерфейсами и сопряжениями.
да скорость можно разгонять значительно выше, но классически рекомендумая - 19200, а для длинных линий (например 1 км) еще ниже, это связано с физическими своствами кабеля...
да скорость можно разгонять значительно выше, но классически рекомендумая - 19200, а для длинных линий (например 1 км) еще ниже, это связано с физическими своствами кабеля...
IMHO - на ваших расстояниях (домашне-огородных) скорость 485 интерфейса 115200 - вполне нормальна, причем с запасом.
При лучшем кабеле можно было бы сделать и мегабит. Но это уже вряд ли стоит.
Да просто попробуйте, взяв бухту кабеля...
да скорость можно разгонять значительно выше, но классически рекомендумая - 19200, а для длинных линий (например 1 км) еще ниже, это связано с физическими своствами кабеля...
IMHO - на ваших расстояниях (домашне-огородных) скорость 485 интерфейса 115200 - вполне нормальна, причем с запасом.
При лучшем кабеле можно было бы сделать и мегабит. Но это уже вряд ли стоит.
Да просто попробуйте, взяв бухту кабеля...
обязательно попробую, но позже... сейчас кроме кабеля для меня имеет значение скорость обработки даных на ардуинке, если скорость передачи будет выше чем скорость обработки - будут потери, по этому пока не будет в более менее полной мере понятны обьемы несчастя, скорость выбрать лучше меньше чем больше а потом пробовать ее поднимать....
кроме того сейчас UART работает паралеьно и на rs485 и на USB к компу (в режиме эмуляции COM), то есть еще тут могут быть заморочки....
кстати попутный вопрос:
использую "альтернативный" монитор порта https://sites.google.com/site/terminalbpp/ вроде все устраивает кроме одного, при подключении ардуинка уходит в ребут, а мне очень хочется подключатся "на живую" без ребута ардуинки...
использую "альтернативный" монитор порта https://sites.google.com/site/terminalbpp/ вроде все устраивает кроме одного, при подключении ардуинка уходит в ребут, а мне очень хочется подключатся "на живую" без ребута ардуинки...
Читал где-то, что это из-за линии DTR, которая на дуинах соединена с reset для удобства загрузки скетчей. Вариантов, помнится, предлагалось несколько: не юзать DTR, перерезать дорожку на плате, ещё чего-то там.
Вот что сходу нашёл:
http://playground.arduino.cc/Main/DisablingAutoResetOnSerialConnection
http://atroshin.ru/ru/content/avtomaticheskaya-perezagruzka-arduino-pri-...
привет, интересная концепция, думаю что не новая но интересная. Тоже умный дом мастерю, пока есть только управление светом... как то у вас обсуждение зачахло. Если что нибудь наработается готов присоединиться к разработке, в силу своих скудных познаний)))
сейчас пишу свой протокол и библиотеки поддержки шины, я тут выкладывал промежуточный вариант но уже далеко ушел, получается очень даже интересно.
Протокол ориентирован на системы с малым ОЗУ, один пакет максимум 8 байт заголовок и до 24 байт данных, в заголовок встроено 4 битовых флага, одна команда может содержать до 8 пакетов данных (192 байта), всего команд - 16 из которых часть (наверно 10) будет свободно для пользовательского использования.
Шина будет уметь
1. находить и устранять конфликты ID
2. выбирать максимальную скорость обмена
3. планирую несколько ID зарезервировать на управление с компа и дебугер, по этому шина будет поддерживать слейвы с 1 по 240 ID.
из железа почти собрал один модуль, он будет мастером и в нем будет встроено SD хранилище + часы реального времени... не выкладываю все это по причине недоделок... осталось кнопки навесить и светодиоды.
то есть получается если мастер ек, все слейвы переходят в режим автономности и не обмениваются данными...
я тут думал над такой концепцией. если слейвы шлют в шину свои данные(на пример с датчиков температуры или движения), но не постоянно а только когда происходят значительные изменения (изменения которые достаточны для того, чтобы что то сделать, на пример изменение температуры на 3 градуса, или наличие движения, или резко запахло газом...) то есть в большинстве своем шина простаивает в ожидании важных команд. Когда слейв крикнул что то другие его слушают, и если информация им нужна, запоминают ее. (на пример контроллер освещения получил информацию об освещенности и движении и в зависимости от времени и режима работы (автоуправление по движению, охрана) принял решение о включении такой-то лампы). Другие же слейвы не связанные с освещенностью (на пример обогрев) эту информацию игнорируют и не учитывают в своей работе. а мастер ее принимает и пишет в лог.
При этом если слейв, которому нужна какая либо информация может получить ее как из лога мастера, так от слейва, если поймал её. Получается что система будет вполне жизнеспособна в плане связи между слейвами, даже если мастер молчит. Как то так...
большинстве своем шина простаивает в ожидании важных команд. Когда слейв крикнул что то другие его слушают, и если информация им нужна, запоминают ее.
При этом если слейв, которому нужна какая либо информация может получить ее как из лога мастера, так от слейва, если поймал её. Получается что система будет вполне жизнеспособна в плане связи между слейвами, даже если мастер молчит. Как то так...
такую систему нельзя реализовать на простых железках типа RS485, такое реально реализуемо только на буферизируемых устройствах (например HUB Ethernet), это уже и сложнее и дороже...
большинстве своем шина простаивает в ожидании важных команд. Когда слейв крикнул что то другие его слушают, и если информация им нужна, запоминают ее.
При этом если слейв, которому нужна какая либо информация может получить ее как из лога мастера, так от слейва, если поймал её. Получается что система будет вполне жизнеспособна в плане связи между слейвами, даже если мастер молчит. Как то так...
такую систему нельзя реализовать на простых железках типа RS485, такое реально реализуемо только на буферизируемых устройствах (например HUB Ethernet), это уже и сложнее и дороже...
RS-485 всего лишь физический уровень, причём, допускающий подключения нескольких устройств параллельно - шина. На нём возможно реализовать любой протол верхнего уровня, в т.ч. и токентинг, и несколько мастеров, и конкурентную среду. Например, модбас допускает несколько мастеров.
RS-485 всего лишь физический уровень, причём, допускающий подключения нескольких устройств параллельно - шина. На нём возможно реализовать любой протол верхнего уровня, в т.ч. и токентинг, и несколько мастеров, и конкурентную среду. Например, модбас допускает несколько мастеров.
ModBus - не допускает одновременно несколько мастеров, если мы говорим о нем как о транспорте нулевого уровня. Он допускает цикличного передача мастера, но мастер всегда в сети один на один момент, если это условие не соблюдать -будет бред в шине... банально две команды от двух мастеров сложатся на физическом уровне...
Зациклились просто с мастерами. Я уже выше давал ссылку на пример с режимом мульти мастер. Только зачем дома нужен космический корабль? Если у меня дома что то потечёт, то контроллер в ванной сразу перекроет воду. А через сколько об этом узнает мастер, через 1 или 2 секунды, какая разница. Что от этого изменится?
RS-485 всего лишь физический уровень, причём, допускающий подключения нескольких устройств параллельно - шина. На нём возможно реализовать любой протол верхнего уровня, в т.ч. и токентинг, и несколько мастеров, и конкурентную среду. Например, модбас допускает несколько мастеров.
ModBus - не допускает одновременно несколько мастеров, если мы говорим о нем как о транспорте нулевого уровня. Он допускает цикличного передача мастера, но мастер всегда в сети один на один момент, если это условие не соблюдать -будет бред в шине... банально две команды от двух мастеров сложатся на физическом уровне...
Я разве где-то говорил об одновременной работе двух мастеров в ModBus?
Я разве где-то говорил об одновременной работе двух мастеров в ModBus?
И я не об этом. Просто зачем всё усложнять, если в с системе будет от трёх-четырёх до десятка МК.
на праздники начал писать отладчик (снифер+дебагер), начал с того, что написал формы разбора произвольного пакета и сборки пакета по параметрам, сейчас прикручиваю работу с COM портом.
короче получается вполне себе система, возможно и для комерческого использования пойдет, вот теперь думаю - выкладывать в нахаляву или нет :)
вот теперь думаю - выкладывать в нахаляву или нет :)
Пилите Шура, пилите (с)
Ну что УСЁ? Все по норам и тему закрыли?
ну че разобрались с интерфейсом?? хотел сделать мнемо схему состояния и контроля устройств по интерфейсу вот с интерфейсом возникли проблеммы, если кому интересно пишите почту скину саму мнемо схему,
по железу:
почти собрал модуль мастера (состоит NANO+часы+SD), осталось поставить диоды на питание (которых пока нет) + 3 кнопки и 5 светодиодов, и лицевую панель, сейчас все собрано в корпусе и работает, но пока без кнопок...
сделал генератор трафика (на NANO)
практически доделал по софту:
1. классы для ардуино и общий шаблон скеча для мастера и слейвов
2. анализатор трафика на дельфи (с разбором пакетов, фильтрами, показателями мусорности и т.д.)
3. отладчик на дельфи (позволяет выделять обмен между мастером и одним слейвом, при этом умеет заставлять мастера посылать в степовом режиме, при этом вся остальная шина работает в штатном режиме)
4. отдельный канал для отладочных сообщений (приходят только отладчику)
осталось реализовать:
1. автоматическая смена ID слейвом
2. обработку широковещательных пакетов
после этого можно будет приступать к слейвам, первый слейв будет: NANO+давление+шина с датчиками температуры (улица и дома по зонам), тут у меня все есть и даже скетч я писал перед новым годом, останется его адаптировать. Следующий этап будет - это экранчик...
Тут в начале говорилось об обмене модулями. Сам начал с освещения. Модуль сделал на печатке. Могу выложить инфу по модулю света, если интересно, хотя сам ниразу не программист, поэтому при вашем уровне, вам врядли понравится.
дошел до состояния когда нужно отлаживать одновременно несколько устройств, есть вопросы
1. можно- ли подключить несколько NANO по USB к одному компу на разные COM порты ? что для этого надо? и как их различать?
2. стоит-ли делать отдельный макетный модуль (NANO+RS485) который использовать как эталонный ответчик мастеру / генератор помех / и т.д. или использовать реальные устройства?
1, Можно, различаются по номеру СОМ порта, у меня от одного-двух до трёх-четырёх висят.
2. Это уже как удобнее. Я связь сначала в симуляторе отлаживаю, а затен в железо переношу. И быстрее, и для МК лучше.
1. можно- ли подключить несколько NANO по USB к одному компу на разные COM порты ? что для этого надо? и как их различать?
Логичней и комп подключать через 485
1. можно- ли подключить несколько NANO по USB к одному компу на разные COM порты ? что для этого надо? и как их различать?
Логичней и комп подключать через 485
комп нужен не только для монитора данных но и для загрузки исправленых программ, при этом будет удобно ничего не перетыкивать а иметь открытым сразу 2 проекта и перезаливать их по мере необходимости....
оставлю 2 полезные ссылки по RS485
http://masters.donntu.org/2007/fvti/arutyunyan/library/art7.htm
http://www.gaw.ru/html.cgi/txt/interface/rs485/power.htm
нужен совет по удобству компоновки скетчей.... сейчас у меня есть некий общий базовый функционал, дальше я вижу 2 пути развития
1. на каждый конкретный железный модуль заводить на основании некого шаблона отдельный проект
2. делать один большой проект который будет подходить всем модулям (в зависимости от директив компилятору)
минусы первого подхода: при внесении изменений в общую часть, или при внесение взаимо зависимых данных нужно будет перелопачивать все проекты
минус второго подхода - будет очень много файлов, и куча директив которые будут существенно усложнять читабельность кода
сам пока склоняюсь ко второму подходу... кто чего посоветует?
Я юзаю второй подход, читабельность кода от этого не страдает, если он правильно отформатирован табуляциями, разнесён на функциональные блоки, оформлен комментариями.
посмотрите ссылочку там есть связь блоков по rs485
http://bigbarrel.ru/tag/%D1%83%D0%BC%D0%BD%D1%8B%D0%B9-%D0%B4%D0%BE%D0%BC/page/2/
посмотрите ссылочку там есть связь блоков по rs485
http://bigbarrel.ru/tag/%D1%83%D0%BC%D0%BD%D1%8B%D0%B9-%D0%B4%D0%BE%D0%BC/page/2/
сейчас у меня реально работает на столе
1. мастер с RTC часами
2. слейв с 4х строчным дисплеем
3. слейв с датчиками температуры даллас на он вире
данные датчиков темппературы и часов корректно отображаются на дисплеи, после разрыва связи все само востанавливается... конфликты одного ID в сети разруливаются... количество модулей с дисплеями ограничено лишь разумным пределом.
Сейчас занимаюсь переписыванием кода на более универсальный с прицелом на расширение, жду тачскринов (буду переделывать на тачи) и еще мелочевки. Так что лично для меня вопрос протокола - не актуален уже, то что сейчас работает я пока не пробовал на больших расстояниях, но на 10 метровом кабеле вообще никаких проблемм (уже 3суток работают на стенде и комп ловит "мусор", пока ни одного ошибочного байта)
зы
выкладывать правда некуда, файлов реально много будет, нужен репозитарий како-то более менее надежный...
выкладывать правда некуда, файлов реально много будет, нужен репозитарий како-то более менее надежный...
GitHub наше всё ;)
не пойму как туда выкладывать вложеные каталоги, конечно можно zip но для меня это не подходит...
наверно можно как отдельную ветку (ибо по смыслу это подходит), может еще какие варианты?
меня интересует как вывести все на мнемосхему с помощью web, ethernet шилда, с помощью Ajax неплохой внешний вид получится!!!! скиньте пожалуйста свой проект очень интересно!!! у меня в проекте управление скважины (Насос включение выключение по давлению, подогрев трубы в мороз, состояния температуры в скважине и самой воды,), уличный датчик (Температура, влажность), и септик (Температура, состояние, аварийное переполнение) вот думал сделать на RS485 и поставить мегу 2560 как мастера с Ethernet шилдом с смартфона или с компьютера открываем страничку и смотрим что там происходит (мнемосхема), или задаем параметры включения и выключения например подогрева трубы в мороз!! вот ищу подоные проекты что бы осущиствить свои планы, у вас помойму есть чему поучится скиньте пожалуйста скетч на почту Anton_Kos87@mail.ru
выложу наброски через неделю, другую сейчас очень сыро еще...
в качестве затравки кину 2 модуля сюда (они пока еще не полностью переписаны на новую идеологию, но все более менее понятно), что-бы было понятно в каком оформление это будет дальше, может кто покритикует...
001
// ---------------------------------------------------------
002
// проект "Модульный умный дом", шина RS485
003
//
004
// состав:
005
// Arduino NANO
006
// MAX485 RS-485 Module TTL to RS-485 module
007
//
008
// версия среды Arduino 1.6.9
009
//
010
// автор <a href="mailto:vde69@mail.ru">vde69@mail.ru</a> (с)
011
// ---------------------------------------------------------
012
013
// ------------------------------------------------------------
014
// конфигурация прошивки
015
//
016
// перечень прошивок, все кроме одной должны быть закоментированы
017
// детальная настройка прошивок в файле A_Config.h
018
// ------------------------------------------------------------
019
020
//#define PROGRAMS_MASTER
021
//#define PROGRAMS_METEOSTATION
022
#define PROGRAMS_TEST
023
//#define PROGRAMS_DISPLAY
024
025
#include "A_Config.h" // загрузим детальную конфигурацию прошивок
026
027
// ------------------------------------------------------------
028
// загрузка библиотек, желательно загрузить все библиотеки до первого оператора
029
// при более позденй загрузки возможны неочевидные ошибки компиляции,
030
// очередность загрузки библиотек важен!!!
031
// до загрузки библиотек не должно быть ничего кроме директив компилятору!!!
032
033
#include <EEPROM.h>
034
#include <Wire.h>
035
#include "BusRS485.h"
036
037
#ifndef A_HOUSE_ADDON // загрузим расширения и общие для всех определения
038
#include
"include/a_house_addon.h"
039
#endif
040
041
042
#ifdef ONE_WIRE
043
#include <OneWire.h>
044
#endif
045
#ifdef TEMPERATURE_DS18B20_ONE_WIRE
046
#include <DallasTemperature.h>
047
#include
"include/ds18b20.h"
048
#endif
049
#ifdef MODULE_RTC_I2C
050
#include <RTClib.h>
051
#endif
052
#ifdef LCD_4X20_I2C
053
#include <LiquidCrystal_I2C.h>
054
#endif
055
056
// ------------------------------------------------------------
057
// Общие переменные не зависящие от конфигурации устройства
058
BusRS485 Bus(0, 5, 4);
// создание шины 485, порт - 0, пин - 5 (Read), пин - 4 (Write), можно назначить на один пин, но не будет работать полное отключение шины
059
unsigned
long
time_loop_new = 0;
// время начала основного цикла, на это время идет синхронизация всех команд и пауз
060
uint8_t Status = 0;
// битовый статус контроллера, включая затребованые команды
061
// 1 - режим инициализации ID
062
// 2 - <свободно>
063
// 3 - <свободно>
064
// 4 - <свободно>
065
// 5 - <свободно>
066
// 6 - <свободно>
067
// 7 - <свободно>
068
// 8 - <свободно>
069
070
// ------------------------------------------------------------
071
// определение переменных и настроек для шин ONE WIRE
072
#ifdef ONE_WIRE
073
OneWire One_Wire(ONE_WIRE);
// Настройка oneWire интерфейса
074
#endif
075
076
// ------------------------------------------------------------
077
// определение переменных и настроек для RTC часов
078
#ifdef MODULE_RTC_I2C
079
RTC_Millis RTC;
080
#endif
081
082
// ------------------------------------------------------------
083
// определение переменных и настроек для LCD текстового дисплея 4х40
084
#ifdef LCD_4X20_I2C
085
LiquidCrystal_I2C lcd(0x27, 20, 4);
086
#endif
087
088
// ------------------------------------------------------------
089
// определение переменных и настроек для датчиков температуры DS18B20
090
#ifdef TEMPERATURE_DS18B20_ONE_WIRE
091
Sensors_DS18B20 sensors_DS18B20(&One_Wire, TEMPERATURE_DS18B20_COUNT);
092
// uint16_t Setup_DS18B20[TEMPERATURE_DS18B20_COUNT][5]; // массив адресов датчиков, первых два байта - зона, остальные адрес
093
#endif
094
095
// ------------------------------------------------------------
096
// определение переменных и настроек для модулей использующих общее время
097
#ifdef MODULE_RTC_USE
098
uint16_t Year = 0;
099
uint8_t Month = 0;
100
uint8_t Day = 0;
101
uint8_t Hour = 0;
102
uint8_t Minute = 0;
103
uint8_t Second = 0;
104
#endif
105
106
107
//*************************************************************************************************
108
// процедура инициализации контролера
109
//*************************************************************************************************
110
void
setup
()
111
{
112
113
if
(Get_signature() != SIGNATURE) {
114
// перезапишем все сохраняемые параметры
115
Set_signature(SIGNATURE);
116
Set_Id(ID_DEFAULT);
117
Set_Speed(SPEED_DEFAULT);
118
delay(100);
119
}
120
121
#ifdef MODULE_MASTER
122
Bus.InitMaster(Get_Speed()*100);
123
#
else
124
if
(Get_Id() == 0) { Bus.InitSlave(Get_Speed()*100, 241, OnSlave_All); }
125
else
{ Bus.InitSlave(Get_Speed()*100, Get_Id(), OnSlave_All); }
126
#endif
127
128
Bus.SetEcho(ECHO);
129
130
#ifdef MODULE_RTC_I2C
131
RTC.begin(DateTime(__DATE__, __TIME__));
132
//RTC.adjust(DateTime(__DATE__, __TIME__)); //— Устанавливает текущие дату и время Вашего компьютера в Часы.
133
#endif
134
135
#ifdef LCD_4X20_I2C
136
lcd.init();
137
lcd.backlight();
138
lcd.noCursor();
139
LCD_4X20_I2C_poll(
true
,
true
);
140
#endif
141
142
}
143
144
145
//*************************************************************************************************
146
// главный цикл
147
//*************************************************************************************************
148
void
loop
() {
149
150
time_loop_new = millis();
// получим время начала цикла
151
152
Bus.poll();
// вызов процедур чтения порта
153
154
#ifdef TEMPERATURE_DS18B20_ONE_WIRE
155
// ds18b20_poll();
156
#endif
157
158
#ifdef MODULE_MASTER
159
Master_poll();
160
#endif
161
162
#ifdef LCD_4X20_I2C
163
LCD_4X20_I2C_poll(
false
,
false
);
164
#endif
165
166
}
01
// ВНИМАНИЕ, в данном модуле использовать можно ТОЛЬКО директивы компилятору #define, #ifdef, #endif, #undef
02
// в случае использовании любых других операторов будут неочевидные ошибки компиляции
03
04
05
// ------------------------------------------------------------------
06
// Значения по умолчанию, можно переопределять в конфигурациях через отмену #undef
07
#define ECHO 0 // эхо приема (выводит в USB)
08
#define ID_DEFAULT 241 // Id по умолчанию, для мастера ставим = 0, для слейва = 241 (ошибочный ID)
09
#define SPEED_DEFAULT 192 // скорость по умолчанию (в сотнях, 192 = 19200), в сотнях сделано для влезания в 2 байта EEPROM, менять не рекомендуется
10
#define TIME_OUT_COMMAND 20 // таймаут ожидания ответа на команду, подбирается опытным путем, рекомендуемый диапазон от 20 до 100
11
#define SIGNATURE 1 // сигнатура формата памяти EEPROM, при смене значения происходит перезапись памяти значениями по умолчанию
12
13
// ------------------------------------------------------------------
14
// Конфигурация мастера
15
// состоит из
16
// I2C RTC DS1307 AT24C32 часы реального времени
17
// Micro SD Storage TF Card Memory Shield Module Adapter SPI For Arduino IDE
18
#ifdef PROGRAMS_MASTER
19
#define MODULE_MASTER
// это мастер шины RS-485
20
#define MODULE_RTC_I2C
// установлен модуль RTC подключение I2C
21
22
// переопределим режим эха
23
#undef ECHO
24
#define ECHO 1
25
26
// переопределим ID по умолчанию
27
#undef ID_DEFAULT
28
#define ID_DEFAULT 0
29
#endif
30
31
// ------------------------------------------------------------------
32
// Конфигурация отладочного модуля
33
#ifdef PROGRAMS_TEST
34
#define MODULE_RTC_USE
// признак, что модуль использует общее время
35
#define LCD_4X20_I2C
// установлен LCD экран 4 строки, 20 символов, подключение I2C
36
37
// -------------------------------------------
38
// следующие 3 параметра связаны друг с другом
39
#define TEMPERATURE_DS18B20_ONE_WIRE
// установлены датчики температуры Dallas на шину One Wire (шина должна быть активирована одельно через ONE_WIRE)
40
#define ONE_WIRE 2
// используется шина One Wire на пин D2 (варианты D2 или D3)
41
#define TEMPERATURE_DS18B20_COUNT 2
// количество установленых датчиков, при изменении количества - обязательно проверяйте распределение памяти EEPROM
42
// -------------------------------------------
43
44
// переопределим режим эха
45
// #undef ECHO
46
// #define ECHO 1
47
#endif
выложу наброски через неделю, другую сейчас очень сыро еще...
в качестве затравки кину 2 модуля сюда (они пока еще не полностью переписаны на новую идеологию, но все более менее понятно), что-бы было понятно в каком оформление это будет дальше, может кто покритикует...
похоже автор статьи забыл выложить вкусняшку и пропал
Доброго времени суток!!!
Долго и внимательно читал тему, но так и не осознал ответа на свой вопрос, как бороться на rs485 с коллизиями? Уважаемый vde69 как ТС и остальная общественность, не могли бы Вы дать комментарий по следующим вопросам
1. Я наивно полагал, что если один что-то передает, то у других Serial.available>0 что говорит о том, что линия занята и надо просто дождаться ее освобождения
01
bool
Transfer_Message(RS485Message Message) {
02
bool
result =
false
;
03
while
(RS485.available() > 0) {
04
RS485.read();
05
}
06
digitalWrite(DIR, HIGH);
// включаем передачу
07
delay(2);
08
RS485.write(Message_to_send.reciver_id);
09
RS485.write(Message_to_send.reciver_adress);
10
RS485.write(Message_to_send.sender_id);
11
RS485.write(Message_to_send.sender_adress);
12
RS485.write(Message_to_send.command);
13
RS485.write(Message_to_send.data1);
14
RS485.write(Message_to_send.data2);
15
RS485.write(Message_to_send.data3);
16
RS485.write(Message_to_send.sign);
17
RS485.flush();
18
digitalWrite(DIR, LOW);
19
delay(2);
20
/*digitalWrite(LED1, HIGH);
21
delay(100);
22
digitalWrite(LED1, LOW);*/
23
result =
true
;
24
return
result;
25
}
но на практике столкнулся с тем, что когда два устройства начинают что-то вещать у меня не просто на шине каша, у меня они перестают вообще что-либо слать.
2. Так можно или нет реализовать схему с мультимастером, и как тогда бороться с коллизиями.
Объясню свою позицию: я согласен с звучавшими в теме мнениями, что вариант запрос-ответ не совсем здорово, потому как нажав кнопку я хочу увидеть реакцию в течении одной секунды а не ждать пока Мастер опросит "мое" устройство. То есть, я нажимаю кнопку, МК посылает данные на Мастер, получает команду и выполняет ее.
В интеренте есть описания протоколов типа KNX, SmartBus но я не нашел готовых реализаций.
В общем темой живо интересуюсь, есть готовые наработки, но при усложнении системы возникли непонятки с коллизиями, и главный вопрос, помему при коллизии больше до МК не достучаться до его перезагрузки.
Я в самом начале писал про интервально-маркерный метод для разделения устройств в моноканале. И приводил ссылку на книгу с подробным описанием этого метода.
Можно просто дать Slave-ам разный приоритет и с более высоким опрашивать чаще, два или более раз в секунду.
Маркерное кольцо - это не мультимастер, по сути это тот же мастер-слейв, только вместо запроса от мастера я гоняю по сети маркер. Или я не прав?
>Что мешает в схеме мастер-слэйв опрашивать подчиненные устройства чаще, чем раз в 1 сек?
по моим подсчетам в сети будет около 100 устройств. Пакет у меня 10 байтный. При скорости 19200 это получается с небольшим запасом 0.01с на запрос-ответ. То есть мастер только и будет занят тем, что опрашивать устройства. Как только на это наложатся временные лаги на выполнение команд самим мастером будет просадка по времени отклика.
>RS485 не позволяет определить наличие колизии, для этого используют CAN
уже читаю про CAN, правда с реализациями на ардуино под can достаточно туго.
>Кривая реализация протокола обмена.
А что тут может быть кривого? Не сочтите за труд посмотрите скеч
01
bool
SendMessage(
byte
Reciver_ID,
byte
Reciver_Adress,
byte
Sender_ID,
byte
Sender_Addres,
byte
Command,
byte
Data1,
byte
Data2,
byte
Data3) {
02
Fill_Message(Reciver_ID, Reciver_Adress, Sender_ID, Sender_Addres, Command, Data1, Data2, Data3);
03
Transfer_Message(Message_to_send);
04
}
05
06
bool
Fill_Message(
byte
Reciver_ID,
byte
Reciver_Adress,
byte
Sender_ID,
byte
Sender_Adress,
byte
Command,
byte
Data1,
byte
Data2,
byte
Data3) {
07
bool
result =
false
;
08
byte
Sign = (Reciver_ID + Reciver_Adress + Sender_ID + Sender_Adress + Command + Data1 + Data2 + Data3) & 255;
09
Message_to_send.reciver_id = Reciver_ID;
10
Message_to_send.reciver_adress = Reciver_Adress;
11
Message_to_send.sender_id = Sender_ID;
12
Message_to_send.sender_adress = Sender_Adress;
13
Message_to_send.command = Command;
14
Message_to_send.data1 = Data1;
15
Message_to_send.data2 = Data2;
16
Message_to_send.data3 = Data3;
17
Message_to_send.sign = Sign;
18
result =
true
;
19
return
result;
20
}
21
22
bool
Transfer_Message(RS485Message Message) {
23
bool
result =
false
;
24
while
(RS485.available() > 0) {
25
RS485.read();
26
}
27
digitalWrite(DIR, HIGH);
// включаем передачу
28
delay(2);
29
RS485.write(Message_to_send.reciver_id);
30
RS485.write(Message_to_send.reciver_adress);
31
RS485.write(Message_to_send.sender_id);
32
RS485.write(Message_to_send.sender_adress);
33
RS485.write(Message_to_send.command);
34
RS485.write(Message_to_send.data1);
35
RS485.write(Message_to_send.data2);
36
RS485.write(Message_to_send.data3);
37
RS485.write(Message_to_send.sign);
38
RS485.flush();
39
digitalWrite(DIR, LOW);
40
delay(2);
41
/*digitalWrite(LED1, HIGH);
42
delay(100);
43
digitalWrite(LED1, LOW);*/
44
result =
true
;
45
return
result;
46
}
47
48
bool
CheckRS485() {
49
digitalWrite(DIR, LOW);
50
delay(2);
51
if
(RS485.available() == 9) {
// если пришло 9 байт
52
Message.reciver_id = RS485.read();
53
Message.reciver_adress = RS485.read();
54
Message.sender_id = RS485.read();
55
Message.sender_adress = RS485.read();
56
Message.command = RS485.read();
57
Message.data1 = RS485.read();
58
Message.data2 = RS485.read();
59
Message.data3 = RS485.read();
60
Message.sign = RS485.read();
61
/*if (RS485.available() ==0){
62
digitalWrite(LED1, HIGH);
63
delay(100);
64
digitalWrite(LED1, LOW);
65
}*/
66
//DebugWrite(Message.command, 1);
67
byte
TestSign = (Message.reciver_id + Message.reciver_adress + Message.sender_id + Message.sender_adress + Message.command + Message.data1 + Message.data2 + Message.data3) & 255;
68
if
(TestSign == Message.sign) {
69
if
(Message.reciver_id == ID) {
70
while
(RS485.available() > 0) {
71
RS485.read();
72
}
73
ParseMessage();
74
return
true
;
75
}
76
else
{
77
DebugWrite(
"NOT FOR ME"
, 1);
78
return
false
;
79
}
80
}
81
else
{
82
DebugWrite(
"BAD SIGN"
, 1);
83
return
false
;
84
}
85
}
86
else
{
87
return
false
;
88
}
89
90
91
}
>А что тут может быть кривого? Не сочтите за труд посмотрите скеч
Кривая сама IDE, при такой загрузке нужен прямой доступ к ресурсам МК.
Маркерное кольцо в каждый конкретный промежуток времени это мастер-слейв. Мастером может быть любое устройство, которому передан маркер - следовательно это мультимастерная схема.