Ta zabawka udaje prawdziwą kasę fiskalną – drukuje paragony, liczy ceny i pozwala wybierać produkty.
Zobacz film z działania:
Schemat wewnętrzny:
Kod źródłowy dla Arduino: https://pastebin.com/vuE97mH7
/* * Zabawkowa kasa fiskalna * 2019-01-31 Aleksander Kawęczyński e.vt0.pl * Arduino pro mini, klawiatura membranowa 4x4, LCD 16x2, drukarka DP-Q58A * * Układ na wejściu zasilania z szeregowo podłączonym mosfetem-p, załączanie: microswitch chwilowy, podtrzymanie włączenia przez pin A3. * * 13 - power control: high - on, low - off; connected to gate of transistor, pobór prądu w stanie wyłączonym: 0,1uA * 2 - printer software serial * 3 .. 10 - 4x4 keyboard matrix * 11 - LCD RS (4) * 12 - LCD E (6) * A0 - LCD D4 (11) * A1 - LCD D5 (12) * A2 - LCD D6 (13) * A3 - LCD D7 (14) * A6 - Vbat przez dzielnik 100K/100K */ #include#include #include #include #define POWER_CTL 13 #define VBATMIN 6000 //minimum battery voltage in mV #define VERSION "1.1" SendOnlySoftwareSerial drukarka(2); // Tx pin LiquidCrystal lcd(11, 12, A0, A1, A2, A3); unsigned long lastAction=0; const unsigned long idle=(5UL*60UL*1000UL); //2 minutes uint16_t keyState=0, lastKeyState=0; char buff[80]; uint16_t suma_cen = 0, powerCounter = 0, printCounter = 0; const uint8_t baza = 5; uint8_t cashier=0; const char s0[] PROGMEM = "BU\x02KA"; const char s1[] PROGMEM = "SER"; const char s2[] PROGMEM = "MLEKO"; const char s3[] PROGMEM = "P\x02\ATKI"; const char s4[] PROGMEM = "ZESZYT"; const char s5[] PROGMEM = "LODY"; const char s6[] PROGMEM = "PIZZA"; const char s7[] PROGMEM = "WARZYWA"; const char s8[] PROGMEM = "SOK"; const char s9[] PROGMEM = "S\x02\ODYCZE"; const char s10[] PROGMEM = "KAKAO"; const char s11[] PROGMEM = "CZYTANKA"; PGM_P const produkty[] PROGMEM = {s0,s1,s2,s3,s4,s5,s6,s7,s8,s9,s10,s11}; const char t0[] PROGMEM = " Sklep \"Julka\""; const char t1[] PROGMEM = " Sklep \"Franek\""; const char t2[] PROGMEM = " Sklep \"Maja\""; const char t3[] PROGMEM = "Z A P R A S Z A"; const char t4[] PROGMEM = " DO ZOBACZENIA!"; PGM_P const powitania[] PROGMEM = {t0,t1,t2,t3,t4}; const uint16_t ceny[] PROGMEM = { 1,//0 6,//1 3,//2 4,//3 2,//4 2,//5 10,//6 7,//7 5,//8 8,//9 9,//10 20//11 }; byte el[8] = { 0b01000, 0b01001, 0b01010, 0b01100, 0b01000, 0b11000, 0b01111, 0b00000 }; void setup() { // pin configuration pinMode(POWER_CTL, OUTPUT); digitalWrite(POWER_CTL, HIGH); //licznik uruchomien powerCounter = EEPROM.read(0); powerCounter += (EEPROM.read(1) << 8); printCounter = EEPROM.read(2); printCounter += (EEPROM.read(3) << 8); if( (printCounter == 255) && (analogRead(A6)<5)){ //po przeprogramowaniu, trzeba wyczyścić zmienne powerCounter =0; printCounter = 0; EEPROM.write(2, ++printCounter); } ++powerCounter; EEPROM.write(0, powerCounter); lcd.begin(16, 2); lcd.createChar(2, el); lcd.clear(); cashier = (powerCounter % 3); strcpy_P(buff, (PGM_P)pgm_read_word(&(powitania[cashier]))); lcd.print(buff); lcd.setCursor(0, 1); strcpy_P(buff, (PGM_P)pgm_read_word(&(powitania[3]))); lcd.print(buff); drukarka.begin(9600); //wait until power button is not pressed delay(100); drukarka.print("\x1B\x40\x1B\x61\x49"); lastAction = millis(); } void loop() { //Keyboard module: uint16_t keys = keyDown(); uint8_t wybranyProdukt = 0; //logic module if(keys) lastAction = millis(); //battery voltage check switch (keys){ case 0: delay(100); if((analogRead(A6)*10) < VBATMIN){ lcd.clear(); lcd.print("Bateria"); lcd.setCursor(0, 1); lcd.print("Roz\x02adowana!"); delay(2000); wylacz(); } break; case (1<<0)://D -- zakonczenie paragonu if(suma_cen == 0){ lcd.clear(); sprintf(buff, "Vb=%d mV; v" VERSION,analogRead(A6)*10 ); lcd.print(buff); lcd.setCursor(0, 1); sprintf(buff, "PWc=%d PrC=%d",powerCounter, printCounter); lcd.print(buff); delay(2000); lcd_powitanie(); } else{ printFooter(); } break; case (1<<1)://# wybranyProdukt = 12; break; case (1<<2)://0 wybranyProdukt = 10; break; case (1<<3)://* wybranyProdukt = 11; break; case (1<<4)://C //change cashier if(suma_cen == 0){ cashier = (++cashier) % 3; lcd_powitanie(); } break; case (1<<5)://9 wybranyProdukt = 9; break; case (1<<6)://8 wybranyProdukt = 8; break; case (1<<7)://7 wybranyProdukt = 7; break; case (1<<8)://B if(suma_cen == 0){ //wydrukuj cos innego drukarka.print("\x1B\x61\x48"); //alignment left drukarka.print(" ,~~.\r\ ( 6 )-_,\r\ ('\___ )=='-' KACZKA\r\ \\ . ) )\r\ \\ `-' / \r\ ~'`~'`~'`~'`~\r\r\r"); drukarka.print("\x1B\x61\x49"); //alignment middle } break; case (1<<9)://6 wybranyProdukt = 6; break; case (1<<10)://5 wybranyProdukt = 5; break; case (1<<11)://4 wybranyProdukt = 4; break; case (1<<12)://A if(suma_cen > 0) printFooter(); //zakoncz paragon przed wylaczeniem wylacz(); break; case (1<<13)://3 wybranyProdukt = 3; break; case (1<<14)://2 wybranyProdukt = 2; break; case (1<<15)://1 wybranyProdukt = 1; break; } if(wybranyProdukt){ int isFirst=0; if(suma_cen == 0) isFirst=1;//pierwszy produkt na tym paragonie, strcpy_P(buff, (PGM_P)pgm_read_word(&(produkty[wybranyProdukt-1]))); uint8_t a = strlen(buff); uint16_t cena = pgm_read_word(&(ceny[wybranyProdukt-1])); suma_cen += cena; lcd.clear(); lcd.print(buff); itoa(cena, buff, 10); lcd.setCursor(15-2-strlen(buff), 0); lcd.print(buff); lcd.print(" Z\x02"); lcd.setCursor(0, 1); sprintf(buff, "SUMA: %7d Z\x02",suma_cen); lcd.print(buff); ; if(isFirst) { printHeader(); isFirst=0; } //drukowanie pozycji na paragonie strcpy_P(buff, (PGM_P)pgm_read_word(&(produkty[wybranyProdukt-1]))); //zamiana \x02 na \x9D for (char* p = buff; p = strchr(p, 0x02); ++p) { *p = 0x9D; } drukarka.print(buff); int spacje = 32-7-a; for(uint8_t k=0;k >3) << 0); pinMode(7, INPUT); //col B pinMode(8, OUTPUT); delay(1); reads += (((PIND & 0x78)>>3) << 4); pinMode(8, INPUT); //col C pinMode(9, OUTPUT); delay(1); reads += (((PIND & 0x78)>>3) << 8); pinMode(9, INPUT); //col D pinMode(10, OUTPUT); delay(1); reads += (((PIND & 0x78)>>3) << 12); pinMode(10, INPUT); return ~reads; } void lcd_powitanie() { lcd.clear(); strcpy_P(buff, (PGM_P)pgm_read_word(&(powitania[cashier]))); lcd.print(buff); lcd.setCursor(0, 1); strcpy_P(buff, (PGM_P)pgm_read_word(&(powitania[3]))); lcd.print(buff); } void lcd16Bit(uint16_t number) { for (uint8_t i=0;i<16;i++) { if (((number >> (15-i)) & 0x01)) { lcd.print("1"); } else { lcd.print("0"); } } } ///drukuje nagłówek paragonu void printHeader(){ EEPROM.write(2, ++printCounter); strcpy_P(buff, (PGM_P)pgm_read_word(&(powitania[cashier]))); drukarka.print("\x1B\x61\x49"); //alignment middle drukarka.print("\x1B\x45\x01"); //bold print drukarka.print("\x1B\x0E\x02"); //times 2 wider pattern drukarka.print(buff); drukarka.print("\x1B\x45\x00"); //bold print off drukarka.print("\x1B\x14\x02"); //cancel times wider pattern delay(30); sprintf(buff, "DZIEN DOBRY! \r\nPARAGON NR %d\r\n\x1D\x6B\x03",printCounter); drukarka.print(buff); sprintf(buff,"%08d", printCounter); drukarka.write((uint8_t)0); sprintf(buff, "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\r",printCounter); drukarka.print(buff); //delay(2000); } void printFooter(){ lcd.clear(); lcd.print("Drukowanie ..."); lcd.setCursor(0, 1); sprintf(buff, "SUMA: %7d Z\x02",suma_cen); lcd.print(buff); sprintf(buff, "\x1B\x61\x50+ \r\x1B\x61\x49================================\r\n"); drukarka.print(buff); delay(100); sprintf(buff, "RAZEM:%23d Z\x9D\r\rDO WIDZENIA!\r\r\r\r",suma_cen); drukarka.print(buff); delay(100); suma_cen = 0; lcd_powitanie(); } uint16_t keyDown(){ uint16_t ret; lastKeyState = keyState; keyState = getKeyState(); ret = ((keyState^lastKeyState)&keyState); return ret; } void powerFunction(){ //Power function: unsigned long czas = millis(); if((lastAction+idle) < czas ) wylacz(); //actions: //lastAction = millis(); } void wylacz(){ lcd.clear(); strcpy_P(buff, (PGM_P)pgm_read_word(&(powitania[(powerCounter % 3)]))); lcd.print(buff); lcd.setCursor(0, 1); strcpy_P(buff, (PGM_P)pgm_read_word(&(powitania[4]))); lcd.print(buff); delay(2000); lcd.clear(); digitalWrite(POWER_CTL, LOW); for(;;){}; }
Więcej zdjęć:
Szablon do wycięcia obudowy ze sklejki (uwaga: wymiary dostosowane do sklejki o grubości 6,3mm!) obudowa_cnc.pdf
Dokumentacja techniczna drukarki: Embedded printer DP-Q58A Technical Manual.pdf
W kodzie Arduino użyłem biblioteki SendOnlySoftwareSerial.zip: SendOnlySoftwareSerial.zip