Передача данных от штангенциркуля
SH-150M через USB и WiFi
Вся моя профессиональная деятельность сводилась к измерениям и
передаче данных в компьютер. Кстати, фотографии - это тоже
измерение и особенно наглядно это видно в фотограмметрии. Как в
этимологии ее названия, так и в самом процессе.
Когда лет 20 назад у меня появился цифровой штангенциркуль, я
попытался передать данные с него в компьютер. Но, полтора
Вольта питания требовали согласования и в результате дальше макета
дело не пошло. И вот по прошествии многих лет мне достался
штангенциркуль, можно сказать моей мечты: батарейка на 3 Вольта,
что позволяет надеяться, что амплитуды сигнала хватит на прямое
подключение к микрокомпьютерам типа Arduino или ESP32. И даже
разъем Mini USB присутствовал.
Правда, в описании было написано, что пользоваться им напрямую
нельзя, а надо покупать специальный кабель.
Так что закралось подозрение, что от USB - только разъем, а
протокол там совсем другой. Ну тогда не будем напрямую подключать
к компьютеру, а попробуем взять обычный кабель USB и посмотреть,
что на него поступает. В стандартном варианте четыре провода от
компьютерного разъема идут к первому, второму, третьему и пятому
контакту разъема Mini USB. Четвертый контакт зарезервирован под
OTG. Подключаем осциллограф и обнаруживаем, что первый контакт -
это земля, второй 3 Вольта, третий, вероятно, какие-то тактовые
импульсы и на последний никакого сигнала, видимо, не поступает.
Очевидно, что подключать к компьютеру такой кабель категорически
нельзя, но и сигналы мы на нем не обнаружили. Так где же он.
Нахожу в своем хозяйстве разъем со всеми пятью доступными
контактами. Ура, на четвертом контакте обнаружен сигнал.
Итак, это последовательный синхронный порт. Можно приступать к
разработке устройства. Передача данных может преследовать разные
цели. Поэтому если сразу сделать универсальное устройство, которое
может все, то разобраться в его работе будет довольно неприятно.
Начнем решать задачи по мере их усложнения, приводя все
промежуточные результаты. Для начала мы хотим получить
просто поток данных, который на компьютере мы
будем принимать через любой монитор порта, например, Arduino IDE.
Простейшее устройство. Подсоединяем 3 провода к Arduino Nano и
воспользуемся слегка модифицированной программой из комментариев к
старой
статье о подсоединении 1,5 В штангенциркуля.
Дискретность передаваемых значений 2 сотых. На экране
штангенциркуля отображаются и нечетные сотые, на экране компьютера
или телефона будут отображаться только четные значения сотых, что
соответствует заявленной точности прибора.
int dataIn = 11; int clockIn = 12; // Variables int clock1 = 1; int lastClock = 1; unsigned long time1 = 0; unsigned long timeStart = 0; int out = 0; long longout=0; float finalout; void setup() { // Pin Set Up pinMode(dataIn, INPUT); pinMode(clockIn, INPUT); Serial.begin(115200); Serial.println("Ready: "); } void loop(){ lastClock = clock1; clock1 = digitalRead(clockIn); if (lastClock == 1 && clock1 == 0){ out = digitalRead(dataIn)+digitalRead(dataIn)+digitalRead(dataIn); // Tripple sampling to remove glitches if((micros() - time1) > 1200){ longout=longout>>8; if (longout & 0b00000000000010000000000000000000){ longout=longout & 0b11111111111101111111111111111111; longout=~longout + 1; } finalout=(float(longout))/50; Serial.print("data:"); Serial.println(finalout); longout=0; } else if((micros() - time1) > 400){ } if (out > 1){ longout=longout|0b01000000000000000000000000000000; longout=longout>>1; } else{ longout=longout>>1; } time1 = micros(); } }
Дальше больше, берем ESP32 и создаем веб-сервер для
передачи данных через wi-fi.
Тут тонкость в том, что надо использовать одно ядро для сбора
данных, а другое для создания веб-сервера и еще задать им
соответствующие приоритеты. Причем, у меня получилось, что ядра не
равно пригодны для этих операций. Работающий у меня вариант
приведен ниже.
int dataIn = 18; int clockIn = 19; // Variables int clock1 = 1; int lastClock = 1; unsigned long time1 = 0; unsigned long timeStart = 0; int out = 0; long longout=0; float finalout; int i=0; float hold1; #define AP_SSID "rwpbb" #define AP_PASS "1234567890" int tempc = 110; #include <GyverPortal.h> GyverPortal ui; TaskHandle_t Task1; TaskHandle_t Task2; // конструктор страницы void build() { GP.BUILD_BEGIN(); GP.THEME(GP_DARK); GP.UPDATE("lb,lbb,lbbb");
GP.LABEL("NAN", "lb",GP_GREEN,44,0);GP.BREAK(); GP.BUTTON_MINI("btn2", "DATA HOLD"); GP.LABEL("NAN", "lbb",GP_RED,44,0);GP.BREAK(); GP.LABEL("NAN", "lbbb",GP_GREEN,44,0);GP.BREAK();
GP.BUILD_END(); }
void setup() { // Pin Set Up pinMode(dataIn, INPUT); pinMode(clockIn, INPUT); Serial.begin(115200); WiFi.mode(WIFI_STA); WiFi.begin(AP_SSID, AP_PASS); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(WiFi.localIP());
// подключаем конструктор и запускаем ui.attachBuild(build); ui.attach(action); ui.start(); // Создаем задачу с кодом из функции Task1code(), // с приоритетом 1 и выполняемую на ядре 0: xTaskCreatePinnedToCore( Task1code, /* Функция задачи */ "Task1", /* Название задачи */ 10000, /* Размер стека задачи */ NULL, /* Параметр задачи */ 1, /* Приоритет задачи */ &Task1, /* Идентификатор задачи, чтобы ее можно было отслеживать */ 0); /* Ядро для выполнения задачи (0) */ delay(500);
// Создаем задачу с кодом из функции Task2code(), // с приоритетом 2 и выполняемую на ядре 1: xTaskCreatePinnedToCore( Task2code, /* Функция задачи */ "Task2", /* Название задачи */ 10000, /* Размер стека задачи */ NULL, /* Параметр задачи */ 2, /* Приоритет задачи */ &Task2, /* Идентификатор задачи, чтобы ее можно было отслеживать */ 1); /* Ядро для выполнения задачи (1) */ delay(500); }
void action() { if (ui.click("btn2")) { hold1= finalout; } if (ui.update()) { if (ui.update("lb")) ui.answer(String(finalout)); if (ui.update("lbb")) ui.answer(String(hold1)); if (ui.update("lbbb")) ui.answer(String(finalout-hold1)); } } // Функция Task2code: void Task2code( void * pvParameters ){ Serial.print("Task2 running on core "); // "Задача Task2 выполняется на ядре " Serial.println(xPortGetCoreID()); for(;;){ lastClock = clock1; clock1 = digitalRead(clockIn); if (lastClock == 1 && clock1 == 0){ out = digitalRead(dataIn)+digitalRead(dataIn)+digitalRead(dataIn); // Tripple sampling to remove glitches if((micros() - time1) > 1200){ longout=longout>>8; if (longout & 0b00000000000010000000000000000000){ longout=longout & 0b11111111111101111111111111111111; longout=~longout + 1; } finalout=(float(longout))/50; i=i+1; if(i==1){ Serial.print("data:"); Serial.println(finalout); i=0; } longout=0; } else if((micros() - time1) > 400){ } if (out > 1){ longout=longout|0b01000000000000000000000000000000; longout=longout>>1; } else{ longout=longout>>1; } time1 = micros(); } } } // Функция Task1code: void Task1code( void * pvParameters ){ Serial.print("Task1 running on core "); // "Задача Task1 выполняется на ядре " Serial.println(xPortGetCoreID());
for(;;){ ui.tick(); } } void loop() { }
Теперь усложняем конструкцию, Добавляем перемычку 1, которая
позволяет питать штангенциркуль от 3,3 В микроконтроллера,
если из штангенциркуля вынуть батарейку. И добавляем сенсорный
контакт 3, который позволит при включении подать команду
на запуск точки доступа. Винт 2 предназначен для фиксации
устройства на штангенциркуле.
Пишем окончательный вариант программы, который позволяет при
запуске точки доступа записать в eprom данные локальной сети
wi-fi. Дополняем интерфейс программы возможностью замораживать
сигнал и показывать изменения относительно него.
int dataIn = 18; int clockIn = 19; // Variables int clock1 = 1; int lastClock = 1; unsigned long time1 = 0; unsigned long timeStart = 0; int out = 0; long longout=0; float finalout; int i=0; float hold1; int tempc = 110; #include <GyverPortal.h> GyverPortal ui; #include <EEPROM.h> struct LoginPass { char ssid[20]; char pass[20]; }; LoginPass lp; TaskHandle_t Task1; TaskHandle_t Task2; // конструктор страницы void build() { GP.BUILD_BEGIN(); GP.THEME(GP_DARK); GP.PAGE_TITLE("CALIPER"); GP.UPDATE("lb,lbb,lbbb");
GP.LABEL("NAN", "lb",GP_GREEN,44,0);GP.BREAK(); GP.BUTTON_MINI("btn2", "DATA HOLD"); GP.LABEL("NAN", "lbb",GP_RED,44,0);GP.BREAK(); GP.LABEL("NAN", "lbbb",GP_GREEN,44,0);GP.BREAK(); GP.FORM_BEGIN("/login"); GP.TEXT("lg", "Login", lp.ssid); GP.BREAK(); GP.TEXT("ps", "Password", lp.pass); GP.SUBMIT("Submit"); GP.FORM_END(); GP.BUILD_END(); }
void setup() { // Pin Set Up pinMode(dataIn, INPUT); pinMode(clockIn, INPUT); Serial.begin(115200); EEPROM.begin(100); EEPROM.get(0, lp); Serial.println(lp.ssid); Serial.println(lp.pass); if (touchRead(15)<35) startap2(); else startup(); // подключаем конструктор и запускаем ui.attachBuild(build); ui.attach(action); ui.start(); // Создаем задачу с кодом из функции Task1code(), // с приоритетом 1 и выполняемую на ядре 0: xTaskCreatePinnedToCore( Task1code, /* Функция задачи */ "Task1", /* Название задачи */ 10000, /* Размер стека задачи */ NULL, /* Параметр задачи */ 1, /* Приоритет задачи */ &Task1, /* Идентификатор задачи, чтобы ее можно было отслеживать */ 0); /* Ядро для выполнения задачи (0) */ delay(500);
// Создаем задачу с кодом из функции Task2code(), // с приоритетом 2 и выполняемую на ядре 1: xTaskCreatePinnedToCore( Task2code, /* Функция задачи */ "Task2", /* Название задачи */ 10000, /* Размер стека задачи */ NULL, /* Параметр задачи */ 2, /* Приоритет задачи */ &Task2, /* Идентификатор задачи, чтобы ее можно было отслеживать */ 1); /* Ядро для выполнения задачи (1) */ delay(500); } void startap2() { Serial.println("Portal start"); // запускаем точку доступа WiFi.mode(WIFI_AP); WiFi.softAP("CALIPER"); //Serial.println(WiFi.localIP()); Serial.println(WiFi.softAPIP()); } void startup() { WiFi.mode(WIFI_STA); WiFi.begin(lp.ssid, lp.pass); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println(WiFi.localIP()); } void action() { if (ui.form("/login")) { // кнопка нажата ui.copyStr("lg", lp.ssid); // копируем себе ui.copyStr("ps", lp.pass); EEPROM.put(0, lp); // сохраняем EEPROM.commit(); // записываем // WiFi.softAPdisconnect(); // отключаем AP } if (ui.click("btn2")) { hold1= finalout; } if (ui.update()) { if (ui.update("lb")) ui.answer(String(finalout)); if (ui.update("lbb")) ui.answer(String(hold1)); if (ui.update("lbbb")) ui.answer(String(finalout-hold1)); } } // Функция Task2code: void Task2code( void * pvParameters ){ Serial.print("Task2 running on core "); // "Задача Task2 выполняется на ядре " Serial.println(xPortGetCoreID()); for(;;){ lastClock = clock1; clock1 = digitalRead(clockIn); if (lastClock == 1 && clock1 == 0){ out = digitalRead(dataIn)+digitalRead(dataIn)+digitalRead(dataIn); // Tripple sampling to remove glitches if((micros() - time1) > 1200){ longout=longout>>8; //%negative numbers switch to 2s complement if (longout & 0b00000000000010000000000000000000){ longout=longout & 0b11111111111101111111111111111111; longout=~longout + 1; } finalout=(float(longout))/50; i=i+1; if(i==1){ Serial.print("data:"); Serial.println(finalout); i=0; } longout=0; } else if((micros() - time1) > 400){ } if (out > 1){ longout=longout|0b01000000000000000000000000000000; longout=longout>>1; } else{ longout=longout>>1; } time1 = micros(); } } } // Функция Task1code: void Task1code( void * pvParameters ){ Serial.print("Task1 running on core "); // "Задача Task1 выполняется на ядре " Serial.println(xPortGetCoreID());
for(;;){ ui.tick(); } } void loop() { }
Как видно на приведённой ниже фотографии, иногда считать
показания штангенциркуля не представляется возможным. Можно
конечно зафиксировать значение винтом и потом достать
штангенциркуль. но способ с дистанционной передачей данных в
момент измерения существенно упрощает и, главное, уточняет
результат. Если доставать штангенциркуль, то о точностях больше
одной десятой говорить уже не приходится. В будущем, возможно,
попробую приспособить штангенциркуль вместо цифровой линейки для
отслеживания перемещения суппорта. В результате должен получиться
относительно дешевый вариант.
7.11.2024
Установите проигрыватель Flash
|
Облако тегов:
...
|