Описание см. Arduino на автомойке и Arduino на автомойке ч.2.
UPD:
Под катом только исходный код. Предназначен только для тех, кому интересно общение arduino и купюроприёмника типа CashCode по протоколу CCNet. Прочим прошу не тратить своё время зря.
#include <SPI.h>
#include <Ethernet.h>
#include <SoftwareSerial.h>
//#include <MFRC522.h>
#include <EEPROM.h>
#include <string.h>
//#include <avr/wdt.h>
//#include "ICMPPing.h"
#define POLYNOMIAL 0x08408
uint8_t reset[] = {0x02, 0x03, 0x06, 0x30, 0x41, 0xB3};
uint8_t ack[] = {0x02, 0x03, 0x06, 0x00, 0x0C2, 0x82};
uint8_t poll[] = {0x02, 0x03, 0x06, 0x33, 0xDA, 0x81};
uint8_t enable[]={0x02, 0x03, 0x0C, 0x34, 0, 0, 0x7C, 0, 0, 0, 0x66, 0xC1};
/*
uint8_t stack[] = {0x02, 0x03, 0x06, 0x35, 0xEC, 0xE4};
uint8_t sec[] = {0x02, 0x03, 0x09, 0x32, 0x00, 0x00, 0x00, 0x26, 0x1F};
uint8_t GetSt[]={0x02, 0x03, 0x06, 0x31, 0xC8, 0xA2};
uint8_t Ident[]={0x02, 0x03, 0x06, 0x37, 0xFE, 0xC7};
uint8_t GetBT[]={0x02, 0x03, 0x06, 0x41, 0x4F, 0xD1};
uint8_t Return[]={0x02, 0x03, 0x06, 0x36, 0x77, 0xD6};
uint8_t Hold[]={0x02, 0x03, 0x06, 0x38, 0x09, 0x3F};
uint8_t ExtBD[]={0x02, 0x03, 0x06, 0x3A, 0x1B, 0x1C};
uint8_t ReqSt[]={0x02, 0x03, 0x06, 0x60, 0xC4, 0xE1};
uint8_t DiBT[]={0x02, 0x03, 0x0C, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x0C};*/
#define max7219_reg_decodeMode 0x09
#define max7219_reg_intensity 0x0a
#define max7219_reg_scanLimit 0x0b
#define max7219_reg_shutdown 0x0c
#define max7219_reg_displayTest 0x0f
const unsigned char alf[] = {0,
28, 34, 34, 34, 34, 34, 34, 28, // 0
8, 24, 8, 8, 8, 8, 8, 28,// 1
28, 34, 2, 4, 8, 16, 32, 62, // 2
28, 34, 2, 4, 2, 2, 34, 28, //3
34, 34, 34, 34, 62, 2, 2, 2,
62, 32, 32, 60, 2, 2, 2, 60,
28, 32, 32, 60, 34, 34, 34, 28,
62, 2, 2, 4, 8, 16, 32, 32,
28, 34, 34, 28, 34, 34, 34, 28,
28, 34, 34, 30, 2, 2, 2, 28
};
#define DIN 16
#define CS 17
#define CLK 18
//#define RST_PIN 8
//#define SS_PIN 9
//MFRC522 mfrc522(SS_PIN, RST_PIN); // Create MFRC522 instance MISO 12, MOSI 11, SCK 13
//byte admin_key[][4] = { { 0x99, 0x3C, 0x16, 0xDB }, { 0x16, 0x95, 0x59, 0x7B }, { 0x46, 0x93, 0xF1, 0x74 }, { 0x03, 0x1F, 0x17, 0xDB }};
void(* resetFunc) (void) = 0;
SoftwareSerial mySerial(14, 15); // RX, TX
byte system100[]={92, 53, 96, 34};
//IPAddress pingAddr(192,168,1,1); // публичный DNS
//SOCKET pingSocket = 0;
//int timePing = 0;
//int pingFAIL = 0; // счётчик фейлов
//ICMPPing ping(pingSocket, (uint16_t)random(0, 255));
//EthernetClient client;
EthernetServer server(80);
char SAM; //номер ящика
unsigned long timeCount=0;
bool waitACK=0;
char count=0;
char LNG=6;
uint8_t c;
uint8_t z1=0;
uint8_t z2=0;
uint8_t old=1;
char rele=0;
char rOld=0;
bool keydown=0;
unsigned long timeAllRele=1000;
unsigned long timeRele=0;
unsigned long lightOff=0;
unsigned long sendPoll=0;
char posPay; //позиция кеша
char posErr; //позиция ошибок
char clientline[100];
unsigned long resetTime;
void setup()
{
//wdt_enable(WDTO_8S);
//delay(2000);
//if(EEPROM.read(30)==1) { EEPROM.write(30, 0); resetFunc(); }
//EEPROM.write(30, 1);
pinMode(DIN,OUTPUT); pinMode(CS,OUTPUT); pinMode(CLK,OUTPUT); digitalWrite(CS, HIGH);
initLed();
pinMode(2, OUTPUT); pinMode(3, OUTPUT); pinMode(4, OUTPUT); pinMode(5, OUTPUT); pinMode(6, OUTPUT); pinMode(7, OUTPUT); digitalWrite(7, HIGH); //свет
pinMode (A5, INPUT); //кнопки
if(EEPROM.read(10)==255 && EEPROM.read(11)==255) { EEPROM.write(10, 0); EEPROM.write(11, 0); } //бабки на табло младший байт первый
posPay=EEPROM.read(99);
if(posPay<0 || posPay>99) { EEPROM.write(99, 0); posPay=0; }
posErr=EEPROM.read(199);
if(posErr<0 || posErr>99) { EEPROM.write(199, 0); posErr=0; }
SAM=EEPROM.read(0);
Serial.begin(9600);
Serial.print(SAM, DEC); Serial.print(" "); Serial.print(posPay, DEC); Serial.print(" "); Serial.print(posErr, DEC);Serial.println(" begin");
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED-SAM };
//byte gate[]={192, 168, 1, 1};
//byte mask[]={255,255,255,0};
IPAddress ip(192, 168, 1, 100+SAM);
Ethernet.begin(mac, ip);//, gate, gate, mask
server.begin();
delay(200);
Serial.print("server is at ");
Serial.println(Ethernet.localIP());
mySerial.begin(9600);
delay(200);
rOld=EEPROM.read(9);
if(rOld<0 || rOld>8) EEPROM.write(9, 0); //сохранение функции
else setRele(rOld);
resetTime=millis();
// mfrc522.PCD_Init();// Init MFRC522
}
void loop()
{
//if(millis()-resetTime>600000) resetFunc(); //перезагрузка через 10 минут
if(millis()-sendPoll>200) { sendCCNET(poll);} // посыл пула //wdt_reset();сброс вотчдога и
/*if (Serial.available()) {
char s=Serial.read(); //int pay=Serial.parseInt();
if(s=='0') { Serial.print("RESET"); sendCCNET(reset); waitACK=1; count=0; }
if(s=='1') { printNumber(100); EEPROM.write(100+posPay, 4); posPay++; EEPROM.write(99, posPay); }
if(s=='3') { sendInfo(); }
}
if(millis()-timePing>10000)
{
timePing=millis();
ICMPEchoReply echoReply = ping(pingAddr, 2);
if (echoReply.status == SUCCESS) pingFAIL=0; else { Serial.println("pingFAIL"); if(++pingFAIL>3) resetFunc(); }
//initLed();
}*/
int key = analogRead (5);
//Serial.println(key);
if(keydown && key<20) keydown=0;
if(key>300 && key<315 && !keydown) setRele(6); //ключ
if(key>330 && key<400 && !keydown) setRele(0);//стоп
if(!keydown && rele==0 && (EEPROM.read(10)>0 || EEPROM.read(11)>0))
{
if(key>920 && key<980) setRele(1);
if(key>810 && key<870) setRele(2);
if(key>720 && key<780) setRele(3);
if(key>600 && key<640) setRele(4);
if(key>420 && key<480) setRele(5);
}
if(rele>0 && rele<=5 && (millis()-timeRele)>timeAllRele) { if(printNumber(-1)==0) setRele(0); timeRele+=timeAllRele; }
if(lightOff>0 && (millis()-lightOff)>10000) { digitalWrite(7, HIGH); lightOff=0; } //свет
for (int sock = 0; sock < MAX_SOCK_NUM; sock++) {
EthernetClient sclient = server.available_(sock);
if (sclient) myserver(sclient);
//serverWorks2(sclient);
}
delay(30);
// Look for new cards
// if ( mfrc522.PICC_IsNewCardPresent() && mfrc522.PICC_ReadCardSerial()) dump_byte_array(mfrc522.uid.uidByte, mfrc522.uid.size);
}
void sendCCNET(uint8_t *com)
{
for(char i=0; i<com[2]; i++) mySerial.write(com[i]);
if(com[3]!=0) { waitACK=1; count=0; sendPoll=millis(); }
while(waitACK)
{
if(millis()-sendPoll>300) waitACK=0; // макс. время приема ответа
if(mySerial.available()) {
c=mySerial.read();
if(count==2) LNG=c;
if(count==3) z1=c;
if(count==4) z2=c;
count++;
}
if(count<LNG) continue;
waitACK=0;
if(z1>0 && z1<255) sendCCNET(ack);
if(z1==0x10 || z1==0x11 || z1==0x12) { sendCCNET(reset); } //Serial.print("POWER UP RESET ");
if(z1==0x19) { sendCCNET(enable); } //Serial.print("DISABLED ENABLE ");
if(z1==70) { sendCCNET(reset); } //Serial.print("PAUSE RESET ");
if(old!=z1)
{
/*Serial.print(z1, HEX); Serial.print(" "); Serial.println(z2, HEX);
if(z1==0x14) Serial.println("IDLING");
if(z1==0x00) Serial.println("ACK"); if(z1==0xFF) Serial.println("NAK");
if(z1==0x13) Serial.println("INIT"); if(z1==0x15) Serial.println("ACCEPTING");
if(z1==0x17) Serial.println("STACKING"); if(z1==0x30) Serial.println("INVALID COMMAND");
if(z1==0x41) Serial.println("DROP CASSETTE FULL");
if(z1==0x44) Serial.println("JAM IN STACKER"); if(z1==0x42) Serial.println("DROP CASSETTE REMOVED");
if(z1==0x47) { Serial.print("Generic Failure codes "); Serial.println(z2, HEX); sendInfo("err", String(z2)); }
if(z1==0x1C) { Serial.print("Generic rejecting code "); Serial.println(z2, HEX); sendInfo("err", String(z2)); } */
if(z1>0x20 && z1<0x81 && posErr<100) { EEPROM.write(200+posErr, z1==0x47 || z1==0x1C ? z2 : z1); posErr++; EEPROM.write(199, posErr); } // sendInfo("err", String(z1));
}
old=z1;
if(z1==0x81) { Serial.print("PACKED, STACKED."); Serial.println(z2); int pay=0;
if(z2==2) pay=10; if(z2==3) pay=50; if(z2==4) pay=100; if(z2==5) pay=500; if(z2==6) pay=1000; if(z2==7) pay=5000;
if(pay>0) { printNumber(pay); if(posPay<100) { EEPROM.write(100+posPay, z2); posPay++; EEPROM.write(99, posPay);}
z2=0; }
}
}
}
void setRele(char r)
{
if(r && r!=rOld) { rOld=r; delay(30); return; } //дребезг
rele=r;
keydown=1;
for(char i=1;i<6;i++) digitalWrite(i+1, HIGH);
if(r==6) r=1; //ключ
if(r) { digitalWrite(r+1, LOW); timeRele=millis(); timeAllRele=60000/EEPROM.read(r); }
EEPROM.write(9, r);
delay(20);
initLed();
if(r==0 && (posPay>5 || posErr>5)) sendInfo();
}
void setCommand(byte command, byte value)
{
digitalWrite(CS, LOW);
for (int i=0; i<4; i++) { shiftOut(DIN,CLK, MSBFIRST, command); shiftOut(DIN,CLK, MSBFIRST, value); }
digitalWrite(CS, HIGH);
}
void initLed()
{
setCommand(max7219_reg_scanLimit, 0x07);
setCommand(max7219_reg_decodeMode, 0x00); // using an led matrix (not digits)
setCommand(max7219_reg_shutdown, 0x01); // not in shutdown mode
setCommand(max7219_reg_displayTest, 0x00); // no display test
setCommand(max7219_reg_intensity, 1);
int n=printNumber(0);
}
int printNumber(int add)
{
int n=EEPROM.read(10)+EEPROM.read(11)*256;
if(add!=0) { n+=add; EEPROM.write(10, n%256); EEPROM.write(11, n>>8); }
int k;
for (char i=1; i<=8; i++)
{
digitalWrite(CS, LOW);
for (char j=3; j>=0; j--) { k=n/pow(10,j); shiftOut(DIN,CLK, MSBFIRST,i); shiftOut(DIN,CLK, MSBFIRST,alf[i+(k%10)*8]); }
digitalWrite(CS, HIGH);
}
if(n>0) { digitalWrite(7, LOW); lightOff=0; } else lightOff=millis(); //свет
return n;
}
void myserver(EthernetClient client)
{
//EthernetClient client = server.available();
int index = 0;
Serial.println("new client");
while (client.available()) {
char c = client.read();
if(index<99) clientline[index] = c;
index++;
//Serial.print(c);
}
//Serial.println(clientline);
char *t;
t=strstr(clientline, "timer");
if (t!=NULL) { t+=5; char timer=*t; t+=2; int value=(*t-48)*10; t++; value+=*t-48; EEPROM.write(timer-48, value);
Serial.print("timer "); Serial.print(timer); Serial.print(" value "); Serial.println(value, DEC);
}
t=strstr(clientline, "pay");
if (t!=NULL) { t+=4; int value=(*t-48)*100; t++; value+=(*t-48)*10; t++; value+=*t-48; printNumber(value);
Serial.print("pay is "); Serial.println(value, DEC);
}
if(strstr(clientline, "reset")!=NULL) resetFunc(); //перезагрузка setup(); //
if(strstr(clientline, "water")!=NULL) digitalWrite(2, LOW); //вода
if(strstr(clientline, "pena")!=NULL) digitalWrite(3, LOW); //пена
if(strstr(clientline, "vosk")!=NULL) digitalWrite(4, LOW); //воск
t=strstr(clientline, "box"); if(t!=NULL) { t+=4; EEPROM.write(0, *t-48); } // setup(); resetFunc(); } //изменение номера бокса
client.println("HTTP/1.1 200 OK"); client.println("Content-Type: text/html"); client.println("Connection: close"); client.println();
client.println("<!DOCTYPE HTML>"); client.println("<html>");
client.print("box is "); client.print(EEPROM.read(0)); client.println("<br />");
client.print("cashcode is "); client.print(z1, HEX); client.println("<br />");
for (int j = 1; j < 6; j++) {
client.print("timer "); client.print(j); client.print(" is "); client.print(EEPROM.read(j)); client.println("<br />");
}
client.println("</html>");
delay(100);
// close the connection:
client.stop();
}
char sendInfo()
{
//if(!posErr && !posPay) return 0;
EthernetClient client;
char j;
String out="sam="+String(EEPROM.read(0))+"&pka="+String(EEPROM.read(13)); //pka
if(posPay)
{
out+="&pays=";
for(j=0; j<posPay; j++) { out+=String(EEPROM.read(100+j))+"-"; }
}
if(posErr)
{
out+="&errs=";
for(j=0; j<posErr; j++) { out+=String(EEPROM.read(200+j))+"-"; }
}
if (client.connect(system100, 80) || client.connect(system100, 80) || client.connect("www.host.ru", 80))
{
Serial.println("GET /control/sinh/get_money.php?"+out+" HTTP/1.1");
client.println("GET /control/sinh/get_money.php?"+out+" HTTP/1.1");
client.println("Host: www.host.ru");
client.println("Connection: keep-alive");
client.println();
} else { Serial.println("connection failed"); return 0; }
delay(100);
char chr13=0;
bool ok=0;
char c;
while (client.available())
{
c=client.read();
Serial.print(c);
if(chr13==2 && c=='o') ok=1; else if(c==13) chr13++; else if(c!=10) chr13=0;
}
client.stop();
//if(!ok) return (0);
posPay=0; EEPROM.write(99, posPay);
posErr=0; EEPROM.write(199, posErr);
return (1);
}
Комментарии (58)
empenoso
10.01.2020 11:31+1А я думал GitHub для таких вещей есть
0xf0a00
10.01.2020 11:37хотите статью из одной ссылки?
Guitariz
10.01.2020 11:53можно например описать, зачем код нужен, какие проблемы решает, иллюстрации добавить… ну или правда не писать статью тогда
paul_155 Автор
10.01.2020 11:56-2Есть же ссылки на две статьи с описанием. Оказалось недостаточно. Просили код — выложил. Да, наверное, не всем это интересно и понятно. Рассчитано на профильных читателей.
Guitariz
10.01.2020 12:12листинг это не отдельная статья, тут правда надо более профильный инструмент юзать
NordicEnergy
10.01.2020 13:04+5Что Вам помешало вставить свой код в одну из статей по ссылке? Вы могли просто нажать кнопку «Редактировать» и добавить в конце или ссылку на guthub или листинг свой. Хабр — это не личный бложик, его же люди читают, поберегите чужое время и свою карму.
paul_155 Автор
10.01.2020 13:45-6и кто бы увидел ссылку в старой статье? надеюсь кому надо оценят.
русским по белому написано сверху: исходный код.
зачем вы потратили своё время не понимаю.NordicEnergy
10.01.2020 13:47и кто бы увидел ссылку в старой статье?
Те, кому нужен этот код точно увидели бы, а так его наблюдают все подряд. Не стоит загаживать людям ленту не нужной информацией и вас полюбят)
WhiteBlackGoose
10.01.2020 14:04+1Можно было бы вставить код в одну из своих статей и призвать тех, кто хочет его увидеть
Guitariz
10.01.2020 14:44+1Все и оценили. Карма и рейтинг статей ровно для этого и нужен.
Для меня хабр источник информации, мне важно, чтобы он оставался незагаженым.
К сожалению, подобная статья является как раз обратным примером, вот и соответствующая реакция.
c_kotik
12.01.2020 12:50Значит в изначальной статье допустили ошибку не разместив там исходники. А сейчас какого то черта вместо нормальной статьи — бАнальный репозиторий. С тем же успехом и результатом можно было создать новую статью с одной лишь ссылкой на гит. Но вместо этого какой то доширак для чтения — просто добавь воды…
x67
10.01.2020 14:22+1Понимаете, вас попросили поделиться кодом, а не создавать целую статью из кода. Код отдельно, метаданные отдельно. Хабр для статей, гитхаб, гитлаб, сорсфордж и иже с ними для кода. Даже если не пользуетесь гитом, достаточно было расшарить зип архив на гуглодиске и прикрепить ссылку к одной из статей. А так вы просто засоряете хабр тем, чего здесь быть не должно ( полный листинг кода)
paul_155 Автор
10.01.2020 13:59-4Хоть бы один стоящий комментарий по теме. Может я тут всем известное колесо изобрёл?
Видимо в ленте со свежими новостями совсем туго, раз пришлось какой-то несчастный листинг открыть.Kenya
10.01.2020 14:24+1Название статьи не говорит о том, что там будет полотно исходного кода. Я ожидал увидеть статью с описанием работы, фото и прочими сопутствующими
paul_155 Автор
10.01.2020 14:31теперь говорит?
c_kotik
12.01.2020 12:56Комментарии, рейтинг и карма просто орут — так делать не хорошо. А вы из себя стпоите Пикассо «я писатель я так вижу ». Прислушайтесь к своей аудитории, если действительно ради них выкладывали.
paul_155 Автор
13.01.2020 11:35думаю моя аудитория это 23 человека, добавивших в закладки.
если даже 3 их них решат какие-то свои проблемы, уже кайф.
если бы писатель… как умею так танцую.
а то, что тут называется «кармой», на самом деле не заслуживает внимания.
EddyEm
10.01.2020 14:32-1Это народ еще в листинг не заглядывал. Вот лично мне сразу бросилось в глаза отсутствие нормального форматирования, уйма закомменченного кода и MAGICK NUMBERS. Жаль, капса побольше нет, потому что «магических чисел» в этом куске кода настолько много, что вообще непонятно, как с ним планируется работать в дальнейшем. Неужто вы вспомните, что такое 0x02, 0x03, 0x06, 0x30, 0x41, 0xB3? Ну или почему в alf[] именно эти значения? А вот это: SoftwareSerial mySerial(14, 15); // RX, TX?
(кстати, ЕМНИП, даже у самой глупой аврки, использующейся в абдурине, есть аппаратный UART; ну и зачем тратить ресурсы на софтовый ногодрыг?)
Честно говоря, страшно смотреть на «код» ардуинщиков. Он чем-то напоминает попытку вырезания гланд через задницу «врачом»-детсадовцем.paul_155 Автор
10.01.2020 14:41аппаратный UART есть да, только он один. а потребовалось два. так бывает.
не понял зачем помнить что такое 0x02, 0x03, 0x06, 0x30, 0x41, 0xB3?
есть описание протокола, там всё расписано.
закомменченный код в основном доп функции. например считывание карт RFID. или вывод всех типов ошибок.
форматирование да хромает. сорянfoal
10.01.2020 14:48И что никаких предопределенных констант нету? Все эти числа в коде…
paul_155 Автор
10.01.2020 14:52не знаю. подскажите где посмотреть, поменяю на константы.
я вобще не видел работающего алгоритма CCNET в открытом доступе.
раз люди обращаются, значит тоже у них проблема.IronHead
10.01.2020 18:42if(keydown && key<20) keydown=0;
if(key>300 && key<315 && !keydown) setRele(6); //ключ
if(key>330 && key<400 && !keydown) setRele(0);//стоп
if(!keydown && rele==0 && (EEPROM.read(10)>0 || EEPROM.read(11)>0))
{
if(key>920 && key<980) setRele(1);
if(key>810 && key<870) setRele(2);
if(key>720 && key<780) setRele(3);
if(key>600 && key<640) setRele(4);
if(key>420 && key<480) setRele(5);
}
20, 300, 315… 640, 420, 480 — что это? количество попугаев?
setRele(1), setRele(2), setRele(3) — какие то реле видимо.но за что отвечают?
paul_155 Автор
10.01.2020 15:14Формат команды: SYNC ADR LNG CMD DATA CRC
SYNC: 1 byte код [02H]
ADR: 1 byte адрес купюроприёмника
LNG: 1 byte* длина данных
CMD: 1 byte команда
DATA 0 to 250 bytes Данные
CRC: 2 bytes контрольная сумма
EddyEm
10.01.2020 14:50-1Ну так обычно под задачу микроконтроллер выбирают, а не пытаются при помощи совочка пирамиду Хеопса соорудить!
У 30-рублевого STM32F030 есть 2 UART'а. Если взять МК за 60 рублей, то будет уже до четырех UART'ов. А за 150 рублей можно и еще больше получить… Не говоря уже о другой периферии, а также DMA.paul_155 Автор
10.01.2020 15:00есть статья на эту тему до моей первой публикации?
да наверное можно было перевыбрать контроллер, но было уже припаяно. и ещё куча проблем.
я на гуру тут не претендую. а в целом и рассчитывал на подобные комментарии. спасибо.
i134
10.01.2020 15:04+1По существу.
1. Выпиливайте все закомментированные куски кода, когда выкладываете исходник для публикации.
2. Зачем делать инициализацию перемменых «char rOld=0;», а потом в setup переинициализировать «rOld=EEPROM.read(9);»?
3. Из АЦП читается мгновенное значение «int key = analogRead (5);» и по мгновенному значению «key» сразу переключаете реле. Поэтому понятно, почему Вы ранее в своей статье писали «для исключения дребезга реле, числа в условиях подбираются опытным путём». Пример одной из Ваших границ из кода, которую Вы подбирали,: «if(key>920 && key<980) setRele(1);». Что составляет ~6% от динамического диапазона АЦП. Аналогично по другим границам.
Поясню. У Вас в схеме, т.к. не видел и могу лишь только предполагать, что в Вашем девайсе есть компоненты с изменяемым потреблением, реле, светодиоды и прочее. А так же т.к. Вы используете Arduino Uno R3, то фильтрация по опорному напряжению для АЦП никакое. У вас скачет питание, работаете с мгновенными значениями. Вот Вы и получаете танцы с бубном при настройке. Так же будете шаманить, когда будете делать второй комплект оборудования.
Решение. Оптимально сделать свой контролер. Но можно и доработать Arduino Uno R3, поставить отдельный фильтр на питание АЦП VAref, а лучше отдельный линейный стабилизатор. Ввести программный фильтр на АЦП: либо усреднять, это проще для понимания, либо добавить хотябы КИХ фильтр первого порядка, например:
#define ALFA 16
float key_filt, key_filt1;
key_filt = key_filt1 + ( key_filt - key_filt1 ) / ALFA;
key_filt1 = key_filt;
key = (int)key_filt;
где ALFA – определяет полосу фильтра.paul_155 Автор
10.01.2020 15:11Закомментированы в основном рабочие куски кода с доп функциями. просто они перестали быть нужны, может кому-то понадобятся. например: if(z1==0x13) Serial.println(«INIT»); — это лог сообщений купюрника.
Кнопки работают через делитель напряжения на резисторах. На всех ящиках одинаковые резисторы. Подбирать только один раз надо. Особых проблем с этим не было.
Arduino давно поменяли на Orange PiIronHead
10.01.2020 18:48Подбирать только один раз надо. Особых проблем с этим не было.
Пока не было
Вам правильно выше описали, что значения АЦП надо фильтровать или усреднять.
Для примера:
Вы считываете значение кнопки — а в этот момент дядя Вася в соседнем боксе варит самопальной сваркой. В ваш диапазон можете и не попасть, просто потому что значения будут прыгать из за наводок.
А завтра вам завезут другие резисторы с другой погрешностью — и может так случится что вы не попадете в диапазон.
А еще контакт кнопки может окислится — у вас же мойка и влажно — сопротивление уйдет и уплывут значения АЦП.
paul_155 Автор
10.01.2020 19:02они в железном ящике. ок, учтём.
тут это совсем не важно. статья в основном про CCNet
в оранже кнопки по другому подключеныi134
10.01.2020 22:55+1они в железном ящике. ок, учтём.
тут это совсем не важно.
Не сочтите что докапываемся по мелочам, но вопросы, относящиеся к метрологии, не так банальны как Вам могут показаться.
На пальцах поясню. Наверняка используете резисторы 5%, как правило у них ткс в зависимости от типа 50-200ppm. Т.к. Ваше железо работает в ящике на улице, то летом может быть +50, а зимой -20, т.е. общая дельта ~70град. Т.е. изменение сопротивления для 5%-резистора может быть в %% 0,35-1,4 при изменении температуры, т.е. с учетом разброса по номиналу сопротивления в 5% общее изменение с учетом климатики работы оборудования 5,35-6,4%. Как видите, легко по погрешности только уже по сопротивлению вываливаетесь из настроенных параметров границы срабатывания.
Да, на это можно и закрыть глаза на 1, 2… прототипах.
статья в основном про CCNet
А Вы не боитесь, что возможно переполнение стека при обращении к sendCCNET, т.к. используете вложенные вызовы?paul_155 Автор
11.01.2020 00:09не боимся. на оранже уже всё делаем и кнопки без резисторов.
ящики были в помещении. на случай размещения на улице предполагалось поставить внутрь датчик температуры и лампочку или кусок греющего кабеля.
выложено как есть, точнее было года 3 назад.
WinLin2
Напомнило журнал «Радио» с печатными листингами программ.
EddyEm
Вот-вот. Страшно подумать, если бы это был нормальный код — эдак на пару десятков тысяч страниц…
paul_155 Автор
Пара десятков тысяч страниц в arduino не влезет. Поэтому много закомментировано.
Если вам это не интересно, зачем надо было открывать?
EddyEm
Абдурины и на основе STM32 делают. Так что, очень даже влезет!
Fox_exe
Не забываем про всякие ESP8266 и прочие, более навороченные девайсы с SPI и NAND памятью, куда полновесную операционку запихнуть можно…
paul_155 Автор
habr.com/ru/post/330124
EddyEm
Скоро абдуринщики для мигания светодиодом будут ПЛИСину брать с 64-битным микроконтроллером и парой гигабайт флеш-памяти… И все равно будут вместо использования аппаратных возможностей тупо дрыгать ногами, как это отчаянно делает автор!
paul_155 Автор
хотя бы 7 человек в закладки статью добавили, значит уже не зря дрыгал.
к сожалению не знаком с вашими творениями. есть от них польза?
EddyEm
Мне и кому-то еще польза есть. Ссылка на гитхаб — в профиле.
Fox_exe
Это верно… В Arduino стандартная либа замедляет всё в десятки раз:
Разок пришлось писать контроллер китайской светодиодной матрицы на Arduino pro mini (24x12 светодиодов. По сути — 3 отдельные панели светодиодов, R, G, B соответственно):
Если адресовать через DigitalWrite() — 8-10 FPS.
Если адресовать через SPI (Один цвет) — 30-50 FPS.
Переписал всё на регистры — 400+ FPS…
… Потом, интереса ради, повторил всё на STM32/HAL/3xSPI+DMA… Получил over9000 FPS…
EddyEm
А если на STM32 без кала сделать, то еще веселей будет!
paul_155 Автор
Недели хватит?
EddyEm
Смотря для чего. Обычно неделя уходит на что-нибудь сложное и новое. А из сниппетов набрать — недолго.
Ну, а минусующие дегенераты … Что ж поделать? На быдлохабре чуть ли не 99% пользователей — быдло тупое, которое вообще ни публикаций, ни комментариев не оставляет, но любит поминусовать. Эффект толпы, однако.
paul_155 Автор
есть сниппет, который работает с CCNet?
jonic
погуглил, я так понимаю это купюроприемник… да берешь библиотеку от ардуино, меняешь платформозависимые функции открытия, чтения/записи и… Готово? хотя я бы взял что то типо роутера и стандартный sdk.
paul_155 Автор
ссылку пожалста.
всё что попадается на эту тему либо через pulse, либо не то.
IronHead
Где ваша шпага, сударь?
В очередной раз флудите в комментариях, переходя на оскорбления, а сами даже ни про что ни написали. Ни одной статьи!
Сдаётся мне сударь, вы пустозвон
d1mk0
В профиле ссылка на ЖЖ.
EddyEm
Я и на бх писал, только под ником Eddy_Em. Там же в профиле указано, почему в этой гнилой дыре я больше никаких статей писать не буду. Всё в ЖЖ.
Fox_exe
А разве HAL — не «Нижняя ступень»? Всмысле, там нет всяких проверок «На идиота», как в ардуине (Если обращаться напрямую к регистрам GPIOA/GPIOB/GPIOC и использовать аппаратный DMA)
EddyEm
Хал — это подобие ардуины от ST. Тоже рассчитано на дурака, поэтому содержит уйму лишних проверок, выливающихся в итоге в жуткий оверхед.
Если бы хал был сделан грамотно на С++ (шаблоны и все дела), оверхеда никакого бы не было. Но «индусы» грамотно не умеют. В итоге получилось опять Г вроде SPL.
А между тем, у ST есть отличные сниппеты для STM32F0. Для меня — загадка, почему, вместо того, чтобы выложить сниппеты под все серии, они стали клепать ненужное Г! Ещё и куб этот придумали, чтобы даже полный идиот мог код сгенерировать. Вот только в сгенерированном коде столько ошибок, что народ не перестает плакаться на форумах!
Ну, в принципе, чем больше идиотов вокруг, тем меньше конкуренция на рынке труда.