Передача данных от штангенциркуля
  через  USB и WiFi (часть вторая)

caliper

Успешно победив новый штангенциркуль, я решил добить и старый. Очевидные проблемы с 1,5 В питания и нестандартным разъемом казались легко преодолимыми. Внешне мой штангенциркуль не отличался от описанного в известной статье. Однако подключение осциллографа показало, что приятная внешность не всегда содержит аналогичное содержание.

caliper

Протокол здесь сильно напоминал описанный в статье Chinese Scale Protocol. И основная неприятность была не в расшифровке, а  в  тактовой частоте, которая была на порядок больше, чем в модели, которую я победил. Не я первый, и решение было известно: вместо опроса тактового сигнала использовать прерывания. Однако моя попытка реализовать эту идею на Arduino Nano не увенчалась успехом. Почти треть тактов я не замечал. Наверное, можно было улучшить программу, ведь были статьи где писалось о положительном результате. Но я пошел путем улучшения железа. Поскольку в конечном случае предполагалось использовать ESP-32, то я перешел на нее сразу.   

caliper

Эксперименты показали, что с желтым светодиодом с падением напряжения в 1,9 В устройство работает стабильнее. Это превышение напряжения, возможно, отрицательно сказалось на работе устройства и потреблении им энергии, но пока не сгорело, а питание штангенциркуля мы все равно теперь получаем не от батарейки, а извне.  Сопротивление 220 Ом, конденсатор 10 мкФ.

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

int dataIn = 18;
int cn = 0;
int out = 0;
long longout=0;
float finalout;
float finalout2;
#define ZERO_PIN 19
hw_timer_t * timer = NULL;
volatile SemaphoreHandle_t timerSemaphore;
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;

void ARDUINO_ISR_ATTR onTimer(){
// Give a semaphore that we can check in the loop
xSemaphoreGiveFromISR(timerSemaphore, NULL);
// It is safe to use digitalRead/Write here if you want to toggle an output
}
void IRAM_ATTR isr() {
cn=cn+1;
out = digitalRead(dataIn)+digitalRead(dataIn)+digitalRead(dataIn);
if (out > 1){
longout=longout|0b01000000000000000000000000000000;
longout=longout>>1;
}
else{
longout=longout>>1;
}
timerRestart(timer);
timerAlarmEnable(timer);
}
void setup() {
pinMode(dataIn, INPUT);
Serial.begin(115200);
Serial.println("START");
attachInterrupt(digitalPinToInterrupt(ZERO_PIN), isr, FALLING);
// Create semaphore to inform us when the timer has fired
timerSemaphore = xSemaphoreCreateBinary();

// Use 1st timer of 4 (counted from zero).
// Set 80 divider for prescaler (see ESP32 Technical Reference Manual for more
// info).
timer = timerBegin(0, 80, true);

// Attach onTimer function to our timer.
timerAttachInterrupt(timer, &onTimer, true);

// Set alarm to call onTimer function every second (value in microseconds).
// Repeat the alarm (third parameter)
timerAlarmWrite(timer, 200000, true);

// Start an alarm
timerAlarmEnable(timer);
}

void loop() {
// If Timer has fired
if (xSemaphoreTake(timerSemaphore, 0) == pdTRUE){

Serial.print(cn);
Serial.println(" cn");
cn=0;
longout=longout>>8;
if (longout & 0b00000000000010000000000000000000){
longout=longout & 0b11111111111101111111111111111111;
//longout=~longout ;
}
finalout=-(float(longout))/100;
finalout2=finalout*0.9922;
if(finalout<-15000){
finalout = 15728.63 +finalout;
finalout2=finalout*0.9922;
}
Serial.print("data:");
Serial.println(finalout2);
longout=0;

}

}

Не мудрствуя, я попытался воспользоваться уже имеющейся программой расшифровки и, как не странно, она заработала, но отсчет шел в обратную сторону и набегала небольшая ошибка. Добавил сдвиг на 15728,63 и умножил на 0,9922, и показания на дисплее штангенциркуля и передаваемые в компьютер совпали с точностью до двух сотых.  Вероятно, расшифровку сигналае можно осуществить более красивым способом, но работает - не трогай.

Дальше больше, берем ESP-32 и создаем веб-сервер для передачи  данных через wi-fi.  Пишем окончательный вариант программы, который позволяет при запуске точки доступа записать в eprom данные локальной сети wi-fi. Дополняем интерфейс программы возможностью замораживать сигнал и показывать изменения относительно него.

int dataIn = 18;
int cn = 0;
int out = 0;
long longout=0;
float finalout;
float finalout2;
#define ZERO_PIN 19
hw_timer_t * timer = NULL;
volatile SemaphoreHandle_t timerSemaphore;
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;

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 ARDUINO_ISR_ATTR onTimer(){
// Give a semaphore that we can check in the loop
xSemaphoreGiveFromISR(timerSemaphore, NULL);
// It is safe to use digitalRead/Write here if you want to toggle an output
}
void IRAM_ATTR isr() {
cn=cn+1;
out = digitalRead(dataIn)+digitalRead(dataIn)+digitalRead(dataIn);
if (out > 1){
longout=longout|0b01000000000000000000000000000000;
longout=longout>>1;
}
else{
longout=longout>>1;
}
timerRestart(timer);
timerAlarmEnable(timer);
}
// конструктор страницы
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);
Serial.begin(115200);
Serial.println("START");
attachInterrupt(digitalPinToInterrupt(ZERO_PIN), isr, FALLING);
// Create semaphore to inform us when the timer has fired
timerSemaphore = xSemaphoreCreateBinary();

// Use 1st timer of 4 (counted from zero).
// Set 80 divider for prescaler (see ESP32 Technical Reference Manual for more
// info).
timer = timerBegin(0, 80, true);

// Attach onTimer function to our timer.
timerAttachInterrupt(timer, &onTimer, true);

// Set alarm to call onTimer function every second (value in microseconds).
// Repeat the alarm (third parameter)
timerAlarmWrite(timer, 200000, true);

// Start an alarm
timerAlarmEnable(timer);
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= finalout2;
}

if (ui.update()) {
if (ui.update("lb")) ui.answer(String(finalout2));
if (ui.update("lbb")) ui.answer(String(hold1));
if (ui.update("lbbb")) ui.answer(String(finalout2-hold1));
}
}
// Функция Task2code:
void Task2code( void * pvParameters ){
Serial.print("Task2 running on core ");
// "Задача Task2 выполняется на ядре "
Serial.println(xPortGetCoreID());
for(;;){
// If Timer has fired
if (xSemaphoreTake(timerSemaphore, 0) == pdTRUE){

Serial.print(cn);
Serial.println(" cn");
cn=0;
longout=longout>>8;
if (longout & 0b00000000000010000000000000000000){
longout=longout & 0b11111111111101111111111111111111;
//longout=~longout ;
}
finalout=-(float(longout))/100;
finalout2=finalout*0.9922;
if(finalout<-15000){
finalout = 15728.63 +finalout;
finalout2=finalout*0.9922;
}
Serial.print("data:");
Serial.println(finalout2);
longout=0;

}
}
}
// Функция Task1code:
void Task1code( void * pvParameters ){
Serial.print("Task1 running on core ");
// "Задача Task1 выполняется на ядре "
Serial.println(xPortGetCoreID());

for(;;){
ui.tick();
}
}
void loop() {

}

В заключение, приведу 3D модель корпуса для получившегося устройства.

19.11.2024
Установите проигрыватель Flash

Облако тегов:
3D печать
Arduino
Raspberry Pi
Аэрофотосъемка
Байдарки
Геомеханика
История
Камеры
Макросъемка
Объективы
Освещение
Панорамы
Принадлежности
Принтеры
Программы
Сканеры
Стереосъемка
Фильтры
Фокусировка
Фотокубики
...
rss