По просьбам выкладываю весь исходный код автомойки на arduino.

Описание см. 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)


  1. WinLin2
    10.01.2020 11:06
    +2

    Напомнило журнал «Радио» с печатными листингами программ.


    1. EddyEm
      10.01.2020 11:28

      Вот-вот. Страшно подумать, если бы это был нормальный код — эдак на пару десятков тысяч страниц…


      1. paul_155 Автор
        10.01.2020 11:54
        -2

        Пара десятков тысяч страниц в arduino не влезет. Поэтому много закомментировано.
        Если вам это не интересно, зачем надо было открывать?


        1. EddyEm
          10.01.2020 12:33

          Абдурины и на основе STM32 делают. Так что, очень даже влезет!


          1. Fox_exe
            10.01.2020 14:25

            Не забываем про всякие ESP8266 и прочие, более навороченные девайсы с SPI и NAND памятью, куда полновесную операционку запихнуть можно…


            1. paul_155 Автор
              10.01.2020 14:42

            1. EddyEm
              10.01.2020 14:45

              Скоро абдуринщики для мигания светодиодом будут ПЛИСину брать с 64-битным микроконтроллером и парой гигабайт флеш-памяти… И все равно будут вместо использования аппаратных возможностей тупо дрыгать ногами, как это отчаянно делает автор!


              1. paul_155 Автор
                10.01.2020 15:34

                хотя бы 7 человек в закладки статью добавили, значит уже не зря дрыгал.
                к сожалению не знаком с вашими творениями. есть от них польза?


                1. EddyEm
                  10.01.2020 17:20
                  -2

                  Мне и кому-то еще польза есть. Ссылка на гитхаб — в профиле.


              1. Fox_exe
                10.01.2020 17:49

                Это верно… В 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…


                1. EddyEm
                  10.01.2020 19:12
                  -1

                  А если на STM32 без кала сделать, то еще веселей будет!


                  1. paul_155 Автор
                    10.01.2020 19:17

                    Недели хватит?


                    1. EddyEm
                      10.01.2020 19:32

                      Смотря для чего. Обычно неделя уходит на что-нибудь сложное и новое. А из сниппетов набрать — недолго.
                      Ну, а минусующие дегенераты … Что ж поделать? На быдлохабре чуть ли не 99% пользователей — быдло тупое, которое вообще ни публикаций, ни комментариев не оставляет, но любит поминусовать. Эффект толпы, однако.


                      1. paul_155 Автор
                        11.01.2020 00:10

                        есть сниппет, который работает с CCNet?


                        1. jonic
                          11.01.2020 00:50

                          погуглил, я так понимаю это купюроприемник… да берешь библиотеку от ардуино, меняешь платформозависимые функции открытия, чтения/записи и… Готово? хотя я бы взял что то типо роутера и стандартный sdk.


                          1. paul_155 Автор
                            11.01.2020 01:44

                            ссылку пожалста.
                            всё что попадается на эту тему либо через pulse, либо не то.


                      1. IronHead
                        11.01.2020 02:58
                        +2

                        Ну, а минусующие дегенераты … Что ж поделать? На быдлохабре чуть ли не 99% пользователей — быдло тупое, которое вообще ни публикаций, ни комментариев не оставляет, но любит поминусовать. Эффект толпы, однако.

                        Где ваша шпага, сударь?
                        В очередной раз флудите в комментариях, переходя на оскорбления, а сами даже ни про что ни написали. Ни одной статьи!
                        Сдаётся мне сударь, вы пустозвон


                        1. d1mk0
                          11.01.2020 04:17

                          В профиле ссылка на ЖЖ.


                        1. EddyEm
                          11.01.2020 09:45

                          Я и на бх писал, только под ником Eddy_Em. Там же в профиле указано, почему в этой гнилой дыре я больше никаких статей писать не буду. Всё в ЖЖ.


                  1. Fox_exe
                    12.01.2020 00:25

                    А разве HAL — не «Нижняя ступень»? Всмысле, там нет всяких проверок «На идиота», как в ардуине (Если обращаться напрямую к регистрам GPIOA/GPIOB/GPIOC и использовать аппаратный DMA)


                    1. EddyEm
                      12.01.2020 10:36
                      -1

                      Хал — это подобие ардуины от ST. Тоже рассчитано на дурака, поэтому содержит уйму лишних проверок, выливающихся в итоге в жуткий оверхед.
                      Если бы хал был сделан грамотно на С++ (шаблоны и все дела), оверхеда никакого бы не было. Но «индусы» грамотно не умеют. В итоге получилось опять Г вроде SPL.
                      А между тем, у ST есть отличные сниппеты для STM32F0. Для меня — загадка, почему, вместо того, чтобы выложить сниппеты под все серии, они стали клепать ненужное Г! Ещё и куб этот придумали, чтобы даже полный идиот мог код сгенерировать. Вот только в сгенерированном коде столько ошибок, что народ не перестает плакаться на форумах!
                      Ну, в принципе, чем больше идиотов вокруг, тем меньше конкуренция на рынке труда.


  1. empenoso
    10.01.2020 11:31
    +1

    А я думал GitHub для таких вещей есть


    1. 0xf0a00
      10.01.2020 11:37

      хотите статью из одной ссылки?


      1. Guitariz
        10.01.2020 11:53

        можно например описать, зачем код нужен, какие проблемы решает, иллюстрации добавить… ну или правда не писать статью тогда


        1. paul_155 Автор
          10.01.2020 11:56
          -2

          Есть же ссылки на две статьи с описанием. Оказалось недостаточно. Просили код — выложил. Да, наверное, не всем это интересно и понятно. Рассчитано на профильных читателей.


          1. Guitariz
            10.01.2020 12:12

            листинг это не отдельная статья, тут правда надо более профильный инструмент юзать


          1. NordicEnergy
            10.01.2020 13:04
            +5

            Что Вам помешало вставить свой код в одну из статей по ссылке? Вы могли просто нажать кнопку «Редактировать» и добавить в конце или ссылку на guthub или листинг свой. Хабр — это не личный бложик, его же люди читают, поберегите чужое время и свою карму.


            1. paul_155 Автор
              10.01.2020 13:45
              -6

              и кто бы увидел ссылку в старой статье? надеюсь кому надо оценят.
              русским по белому написано сверху: исходный код.
              зачем вы потратили своё время не понимаю.


              1. NordicEnergy
                10.01.2020 13:47

                и кто бы увидел ссылку в старой статье?
                Те, кому нужен этот код точно увидели бы, а так его наблюдают все подряд. Не стоит загаживать людям ленту не нужной информацией и вас полюбят)


              1. WhiteBlackGoose
                10.01.2020 14:04
                +1

                Можно было бы вставить код в одну из своих статей и призвать тех, кто хочет его увидеть


              1. Guitariz
                10.01.2020 14:44
                +1

                Все и оценили. Карма и рейтинг статей ровно для этого и нужен.
                Для меня хабр источник информации, мне важно, чтобы он оставался незагаженым.
                К сожалению, подобная статья является как раз обратным примером, вот и соответствующая реакция.


              1. c_kotik
                12.01.2020 12:50

                Значит в изначальной статье допустили ошибку не разместив там исходники. А сейчас какого то черта вместо нормальной статьи — бАнальный репозиторий. С тем же успехом и результатом можно было создать новую статью с одной лишь ссылкой на гит. Но вместо этого какой то доширак для чтения — просто добавь воды…


          1. x67
            10.01.2020 14:22
            +1

            Понимаете, вас попросили поделиться кодом, а не создавать целую статью из кода. Код отдельно, метаданные отдельно. Хабр для статей, гитхаб, гитлаб, сорсфордж и иже с ними для кода. Даже если не пользуетесь гитом, достаточно было расшарить зип архив на гуглодиске и прикрепить ссылку к одной из статей. А так вы просто засоряете хабр тем, чего здесь быть не должно ( полный листинг кода)


      1. t3hk0d3
        10.01.2020 12:47
        +1

        Ссылку можно в существующий пост добавить, либо оставить комментарием.


  1. paul_155 Автор
    10.01.2020 13:59
    -4

    Хоть бы один стоящий комментарий по теме. Может я тут всем известное колесо изобрёл?
    Видимо в ленте со свежими новостями совсем туго, раз пришлось какой-то несчастный листинг открыть.


    1. Kenya
      10.01.2020 14:24
      +1

      Название статьи не говорит о том, что там будет полотно исходного кода. Я ожидал увидеть статью с описанием работы, фото и прочими сопутствующими


      1. paul_155 Автор
        10.01.2020 14:31

        теперь говорит?


        1. c_kotik
          12.01.2020 12:56

          Комментарии, рейтинг и карма просто орут — так делать не хорошо. А вы из себя стпоите Пикассо «я писатель я так вижу ». Прислушайтесь к своей аудитории, если действительно ради них выкладывали.


          1. paul_155 Автор
            13.01.2020 11:35

            думаю моя аудитория это 23 человека, добавивших в закладки.
            если даже 3 их них решат какие-то свои проблемы, уже кайф.
            если бы писатель… как умею так танцую.
            а то, что тут называется «кармой», на самом деле не заслуживает внимания.


    1. EddyEm
      10.01.2020 14:32
      -1

      Это народ еще в листинг не заглядывал. Вот лично мне сразу бросилось в глаза отсутствие нормального форматирования, уйма закомменченного кода и MAGICK NUMBERS. Жаль, капса побольше нет, потому что «магических чисел» в этом куске кода настолько много, что вообще непонятно, как с ним планируется работать в дальнейшем. Неужто вы вспомните, что такое 0x02, 0x03, 0x06, 0x30, 0x41, 0xB3? Ну или почему в alf[] именно эти значения? А вот это: SoftwareSerial mySerial(14, 15); // RX, TX?
      (кстати, ЕМНИП, даже у самой глупой аврки, использующейся в абдурине, есть аппаратный UART; ну и зачем тратить ресурсы на софтовый ногодрыг?)

      Честно говоря, страшно смотреть на «код» ардуинщиков. Он чем-то напоминает попытку вырезания гланд через задницу «врачом»-детсадовцем.


      1. paul_155 Автор
        10.01.2020 14:41

        аппаратный UART есть да, только он один. а потребовалось два. так бывает.
        не понял зачем помнить что такое 0x02, 0x03, 0x06, 0x30, 0x41, 0xB3?
        есть описание протокола, там всё расписано.
        закомменченный код в основном доп функции. например считывание карт RFID. или вывод всех типов ошибок.
        форматирование да хромает. сорян


        1. foal
          10.01.2020 14:48

          И что никаких предопределенных констант нету? Все эти числа в коде…


          1. paul_155 Автор
            10.01.2020 14:52

            не знаю. подскажите где посмотреть, поменяю на константы.
            я вобще не видел работающего алгоритма CCNET в открытом доступе.
            раз люди обращаются, значит тоже у них проблема.


            1. IronHead
              10.01.2020 18:42

              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);
              }

              20, 300, 315… 640, 420, 480 — что это? количество попугаев?
              setRele(1), setRele(2), setRele(3) — какие то реле видимо.но за что отвечают?


              1. paul_155 Автор
                10.01.2020 19:04

                там сверху указано где смотреть


          1. 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 контрольная сумма


        1. EddyEm
          10.01.2020 14:50
          -1

          Ну так обычно под задачу микроконтроллер выбирают, а не пытаются при помощи совочка пирамиду Хеопса соорудить!
          У 30-рублевого STM32F030 есть 2 UART'а. Если взять МК за 60 рублей, то будет уже до четырех UART'ов. А за 150 рублей можно и еще больше получить… Не говоря уже о другой периферии, а также DMA.


          1. paul_155 Автор
            10.01.2020 15:00

            есть статья на эту тему до моей первой публикации?
            да наверное можно было перевыбрать контроллер, но было уже припаяно. и ещё куча проблем.
            я на гуру тут не претендую. а в целом и рассчитывал на подобные комментарии. спасибо.


    1. 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 – определяет полосу фильтра.


      1. paul_155 Автор
        10.01.2020 15:11

        Закомментированы в основном рабочие куски кода с доп функциями. просто они перестали быть нужны, может кому-то понадобятся. например: if(z1==0x13) Serial.println(«INIT»); — это лог сообщений купюрника.
        Кнопки работают через делитель напряжения на резисторах. На всех ящиках одинаковые резисторы. Подбирать только один раз надо. Особых проблем с этим не было.
        Arduino давно поменяли на Orange Pi


        1. IronHead
          10.01.2020 18:48

          Подбирать только один раз надо. Особых проблем с этим не было.

          Пока не было
          Вам правильно выше описали, что значения АЦП надо фильтровать или усреднять.
          Для примера:
          Вы считываете значение кнопки — а в этот момент дядя Вася в соседнем боксе варит самопальной сваркой. В ваш диапазон можете и не попасть, просто потому что значения будут прыгать из за наводок.
          А завтра вам завезут другие резисторы с другой погрешностью — и может так случится что вы не попадете в диапазон.
          А еще контакт кнопки может окислится — у вас же мойка и влажно — сопротивление уйдет и уплывут значения АЦП.


          1. paul_155 Автор
            10.01.2020 19:02

            они в железном ящике. ок, учтём.
            тут это совсем не важно. статья в основном про CCNet
            в оранже кнопки по другому подключены


            1. 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, т.к. используете вложенные вызовы?


              1. paul_155 Автор
                11.01.2020 00:09

                не боимся. на оранже уже всё делаем и кнопки без резисторов.
                ящики были в помещении. на случай размещения на улице предполагалось поставить внутрь датчик температуры и лампочку или кусок греющего кабеля.
                выложено как есть, точнее было года 3 назад.


  1. c_kotik
    12.01.2020 12:57

    Как легким движением руки автомойку превратить в хабрапомойку… Используя ардуину.


    1. paul_155 Автор
      13.01.2020 11:52

      всё в мире относительно.
      комментариями тут создали помойку, зато в поисковиках заметно подняли в выдаче.
      хабр забудет через два дня, яндекс никогда


  1. AndyBuh
    13.01.2020 11:36

    А выдача чеков и работа с фискальным накопителем предусмотрены?


    1. paul_155 Автор
      13.01.2020 11:37

      предусмотрена выгрузка данных о платежах на сервер, там уже можно подключить кассовый аппарат и выдавать чеки по запросу.