La totalité du projet sera documentée, mais pour l'instant, prise de note ! **Transcodex graphique** -> dans le raspberryPi, pour lancer le script python automatiquement: login automatique en CLI Ajouter dans .bashrc les deux lignes suivantes: cd /home/pi/git/py-thermal-printer /usr/bin/python /home/pi/git/py-thermal-printer/codex.py **-> le script python:** #!/usr/bin/env python #coding=utf-8 import serial, struct, time # to read key on unix import os import sys import termios import tty # to print, ... ha ha ha import Image, ImageDraw import os.path #===========================================================# # RASPBERRY PI (tested with Raspbian Jan 2012): # - Ensure that ttyAMA0 is not used for serial console access: # edit /boot/cmdline.txt (remove all name-value pairs containing # ttyAMA0) and comment out last line in /etc/inittab. # - Fix user permissions with "sudo usermod -a -G dialout pi" # - Reboot # - Ensure that the SERIALPORT setting is correct below # # BEAGLE BONE: # Mux settings (Ängström 2012.05, also work on ubuntu 12.04): # echo 1 > /sys/kernel/debug/omap_mux/spi0_sclk # echo 1 > /sys/kernel/debug/omap_mux/spi0_d0 #===========================================================# print "Première ligne du script" class ThermalPrinter(object): """ Thermal printing library that controls the "micro panel thermal printer" sold in shops like Adafruit and Sparkfun (e.g. http://www.adafruit.com/products/597). Mostly ported from Ladyada's Arduino library (https://github.com/adafruit/Adafruit-Thermal-Printer-Library) to run on BeagleBone and Raspberry Pi. Currently handles printing image data and text, but the rest of the built-in functionality like underlining and barcodes are trivial to port to Python when needed. If on BeagleBone or similar device, remember to set the mux settings or change the UART you are using. See the beginning of this file for default setup. Thanks to Matt Richardson for the initial pointers on controlling the device via Python. @author: Lauri Kainulainen """ # default serial port for the Beagle Bone #SERIALPORT = '/dev/ttyO2' # this might work better on a Raspberry Pi SERIALPORT = '/dev/ttyAMA0' BAUDRATE = 19200 TIMEOUT = 10 # pixels with more color value (average for multiple channels) are counted as white # tweak this if your images appear too black or too white black_threshold = 64 # pixels with less alpha than this are counted as white alpha_threshold = 127 printer = None _ESC = chr(27) # These values (including printDensity and printBreaktime) are taken from # lazyatom's Adafruit-Thermal-Library branch and seem to work nicely with bitmap # images. Changes here can cause symptoms like images printing out as random text. # Play freely, but remember the working values. # https://github.com/adafruit/Adafruit-Thermal-Printer-Library/blob/0cc508a9566240e5e5bac0fa28714722875cae69/Thermal.cpp # Set "max heating dots", "heating time", "heating interval" # n1 = 0-255 Max printing dots, Unit (8dots), Default: 7 (64 dots) # n2 = 3-255 Heating time, Unit (10us), Default: 80 (800us) # n3 = 0-255 Heating interval, Unit (10us), Default: 2 (20us) # The more max heating dots, the more peak current will cost # when printing, the faster printing speed. The max heating # dots is 8*(n1+1). The more heating time, the more density, # but the slower printing speed. If heating time is too short, # blank page may occur. The more heating interval, the more # clear, but the slower printing speed. def __init__(self, heatTime=80, heatInterval=2, heatingDots=7, serialport=SERIALPORT): print "Init Started" self.printer = serial.Serial(serialport, self.BAUDRATE, timeout=self.TIMEOUT) self.printer.write(self._ESC) # ESC - command self.printer.write(chr(64)) # @ - initialize self.printer.write(self._ESC) # ESC - command self.printer.write(chr(55)) # 7 - print settings self.printer.write(chr(heatingDots)) # Heating dots (20=balance of darkness vs no jams) default = 20 self.printer.write(chr(heatTime)) # heatTime Library default = 255 (max) self.printer.write(chr(heatInterval)) # Heat interval (500 uS = slower, but darker) default = 250 # Description of print density from page 23 of the manual: # DC2 # n Set printing density # Decimal: 18 35 n # D4..D0 of n is used to set the printing density. Density is 50% + 5% * n(D4-D0) printing density. # D7..D5 of n is used to set the printing break time. Break time is n(D7-D5)*250us. printDensity = 15 # 120% (? can go higher, text is darker but fuzzy) printBreakTime = 15 # 500 uS self.printer.write(chr(18)) self.printer.write(chr(35)) self.printer.write(chr((printDensity << 4) | printBreakTime)) def offline(self): # Take the printer offline. Print commands sent after this will be # ignored until 'online' is called. self.printer.write(self._ESC) self.printer.write(chr(61)) self.printer.write(chr(0)) def online(self): # Take the printer back online. Subsequent print commands will be obeyed. self.printer.write(self._ESC) self.printer.write(chr(61)) self.printer.write(chr(1)) def sleep(self): # Put the printer into a low-energy state immediately. self.sleep_after(1) # Can't be 0, that means 'don't sleep' def sleep_after(self, seconds): # Put the printer into a low-energy state after the given number # of seconds. if seconds: time.sleep(seconds) self.printer.write(self._ESC) self.printer.write(chr(56)) self.printer.write(chr(seconds)) self.printer.write(chr(seconds >> 8)) def wake(self): # Wake the printer from a low-energy state. self.printer.write(chr(255)) time.sleep(0.05) self.printer.write(self._ESC) self.printer.write(chr(56)) self.printer.write(chr(0)) self.printer.write(chr(0)) def has_paper(self): # Check the status of the paper using the printer's self reporting # ability. SerialTX _must_ be connected! self.printer.write(self._ESC) self.printer.write(chr(118)) self.printer.write(chr(0)) status = -1 if self.printer.inWaiting() > 0: ret = self.printer.read() if ret: status = struct.unpack('b', ret)[0] return bool(status & 0b00000100) def reset(self): self.printer.write(self._ESC) self.printer.write(chr(64)) def linefeed(self): self.printer.write(chr(10)) def justify(self, align="L"): pos = 0 if align == "L": pos = 0 elif align == "C": pos = 1 elif align == "R": pos = 2 self.printer.write(self._ESC) self.printer.write(chr(97)) self.printer.write(chr(pos)) def bold_off(self): self.printer.write(self._ESC) self.printer.write(chr(69)) self.printer.write(chr(0)) def bold_on(self): self.printer.write(self._ESC) self.printer.write(chr(69)) self.printer.write(chr(1)) def font_b_off(self): self.printer.write(self._ESC) self.printer.write(chr(33)) self.printer.write(chr(0)) def font_b_on(self): self.printer.write(self._ESC) self.printer.write(chr(33)) self.printer.write(chr(1)) def underline_off(self): self.printer.write(self._ESC) self.printer.write(chr(45)) self.printer.write(chr(0)) def underline_on(self): self.printer.write(self._ESC) self.printer.write(chr(45)) self.printer.write(chr(1)) def inverse_off(self): self.printer.write(chr(29)) self.printer.write(chr(66)) self.printer.write(chr(0)) def inverse_on(self): self.printer.write(chr(29)) self.printer.write(chr(66)) self.printer.write(chr(1)) def upsidedown_off(self): self.printer.write(self._ESC) self.printer.write(chr(123)) self.printer.write(chr(0)) def upsidedown_on(self): self.printer.write(self._ESC) self.printer.write(chr(123)) self.printer.write(chr(1)) def barcode_chr(self, msg): self.printer.write(chr(29)) # Leave self.printer.write(chr(72)) # Leave self.printer.write(msg) # Print barcode # 1:Abovebarcode 2:Below 3:Both 0:Not printed def barcode_height(self, msg): self.printer.write(chr(29)) # Leave self.printer.write(chr(104)) # Leave self.printer.write(msg) # Value 1-255 Default 50 def barcode_height(self): self.printer.write(chr(29)) # Leave self.printer.write(chr(119)) # Leave self.printer.write(chr(2)) # Value 2,3 Default 2 def barcode(self, msg): """ Please read http://www.adafruit.com/datasheets/A2-user%20manual.pdf for information on how to use barcodes. """ # CODE SYSTEM, NUMBER OF CHARACTERS # 65=UPC-A 11,12 #71=CODEBAR >1 # 66=UPC-E 11,12 #72=CODE93 >1 # 67=EAN13 12,13 #73=CODE128 >1 # 68=EAN8 7,8 #74=CODE11 >1 # 69=CODE39 >1 #75=MSI >1 # 70=I25 >1 EVEN NUMBER self.printer.write(chr(29)) # LEAVE self.printer.write(chr(107)) # LEAVE self.printer.write(chr(65)) # USE ABOVE CHART self.printer.write(chr(12)) # USE CHART NUMBER OF CHAR self.printer.write(msg) def print_text(self, msg, chars_per_line=None): """ Print some text defined by msg. If chars_per_line is defined, inserts newlines after the given amount. Use normal '\n' line breaks for empty lines. """ if chars_per_line == None: self.printer.write(msg) else: l = list(msg) le = len(msg) for i in xrange(chars_per_line + 1, le, chars_per_line + 1): l.insert(i, '\n') self.printer.write("".join(l)) print "".join(l) def print_markup(self, markup): """ Print text with markup for styling. Keyword arguments: markup -- text with a left column of markup as follows: first character denotes style (n=normal, b=bold, u=underline, i=inverse, f=font B) second character denotes justification (l=left, c=centre, r=right) third character must be a space, followed by the text of the line. """ lines = markup.splitlines(True) for l in lines: style = l[0] justification = l[1].upper() text = l[3:] if style == 'b': self.bold_on() elif style == 'u': self.underline_on() elif style == 'i': self.inverse_on() elif style == 'f': self.font_b_on() self.justify(justification) self.print_text(text) if justification != 'L': self.justify() if style == 'b': self.bold_off() elif style == 'u': self.underline_off() elif style == 'i': self.inverse_off() elif style == 'f': self.font_b_off() def convert_pixel_array_to_binary(self, pixels, w, h): """ Convert the pixel array into a black and white plain list of 1's and 0's width is enforced to 384 and padded with white if needed. """ black_and_white_pixels = [1] * 384 * h if w > 384: print "Bitmap width too large: %s. Needs to be under 384" % w return False elif w < 384: print "Bitmap under 384 (%s), padding the rest with white" % w print "Bitmap size", w if type(pixels[0]) == int: # single channel print " => single channel" for i, p in enumerate(pixels): if p < self.black_threshold: black_and_white_pixels[i % w + i / w * 384] = 0 else: black_and_white_pixels[i % w + i / w * 384] = 1 elif type(pixels[0]) in (list, tuple) and len(pixels[0]) == 3: # RGB print " => RGB channel" for i, p in enumerate(pixels): if sum(p[0:2]) / 3.0 < self.black_threshold: black_and_white_pixels[i % w + i / w * 384] = 0 else: black_and_white_pixels[i % w + i / w * 384] = 1 elif type(pixels[0]) in (list, tuple) and len(pixels[0]) == 4: # RGBA print " => RGBA channel" for i, p in enumerate(pixels): if sum(p[0:2]) / 3.0 < self.black_threshold and p[3] > self.alpha_threshold: black_and_white_pixels[i % w + i / w * 384] = 0 else: black_and_white_pixels[i % w + i / w * 384] = 1 else: print "Unsupported pixels array type. Please send plain list (single channel, RGB or RGBA)" print "Type pixels[0]", type(pixels[0]), "haz", pixels[0] return False return black_and_white_pixels def print_bitmap(self, pixels, w, h, output_png=False): """ Best to use images that have a pixel width of 384 as this corresponds to the printer row width. pixels = a pixel array. RGBA, RGB, or one channel plain list of values (ranging from 0-255). w = width of image h = height of image if "output_png" is set, prints an "print_bitmap_output.png" in the same folder using the same thresholds as the actual printing commands. Useful for seeing if there are problems with the original image (this requires PIL). Example code with PIL: import Image, ImageDraw i = Image.open("lammas_grayscale-bw.png") data = list(i.getdata()) w, h = i.size p.print_bitmap(data, w, h) """ counter = 0 if output_png: import Image, ImageDraw test_img = Image.new('RGB', (384, h)) draw = ImageDraw.Draw(test_img) self.linefeed() black_and_white_pixels = self.convert_pixel_array_to_binary(pixels, w, h) print_bytes = [] # read the bytes into an array for rowStart in xrange(0, h, 256): chunkHeight = 255 if (h - rowStart) > 255 else h - rowStart print_bytes += (18, 42, chunkHeight, 48) for i in xrange(0, 48 * chunkHeight, 1): # read one byte in byt = 0 for xx in xrange(8): pixel_value = black_and_white_pixels[counter] counter += 1 # check if this is black if pixel_value == 0: byt += 1 << (7 - xx) if output_png: draw.point((counter % 384, round(counter / 384)), fill=(0, 0, 0)) # it's white else: if output_png: draw.point((counter % 384, round(counter / 384)), fill=(255, 255, 255)) print_bytes.append(byt) # output the array all at once to the printer # might be better to send while printing when dealing with # very large arrays... for b in print_bytes: self.printer.write(chr(b)) if output_png: test_print = open('print-output.png', 'wb') test_img.save(test_print, 'PNG') print "output saved to %s" % test_print.name test_print.close() def getKey(): fd = sys.stdin.fileno() old = termios.tcgetattr(fd) new = termios.tcgetattr(fd) new[3] = new[3] & ~termios.ICANON & ~termios.ECHO new[6][termios.VMIN] = 1 new[6][termios.VTIME] = 0 termios.tcsetattr(fd, termios.TCSANOW, new) key = None try: key = os.read(fd, 3) finally: termios.tcsetattr(fd, termios.TCSAFLUSH, old) return key def PrintImage( ImageName) : i = Image.open( ImageName) data = list(i.getdata()) w, h = i.size p.print_bitmap(data, w, h, False) p.linefeed() p.linefeed() p.linefeed() if __name__ == '__main__': import sys, os if len(sys.argv) == 2: serialport = sys.argv[1] print "point d'arrêt n°1" else: serialport = ThermalPrinter.SERIALPORT print "point d'arrêt n°2" if not os.path.exists(serialport): sys.exit("ERROR: Serial port not found at: %s" % serialport) print "Testing printer on port %s" % serialport p = ThermalPrinter(serialport=serialport) p.print_text("\nLes Voyages typo-graphiques\n") p.print_text("\nde Jules Verne\n") # runtime dependency on Python Imaging Library ImgArrays = { 'A':'/home/pi/git/py-thermal-printer/pics/VT_Codex_Thermique_A.png' , 'B':'/home/pi/git/py-thermal-printer/pics/VT_Codex_Thermique_B.png' , 'C':'/home/pi/git/py-thermal-printer/pics/VT_Codex_Thermique_C.png' , 'D':'/home/pi/git/py-thermal-printer/pics/VT_Codex_Thermique_D.png' , 'E':'/home/pi/git/py-thermal-printer/pics/VT_Codex_Thermique_E.png' , 'F':'/home/pi/git/py-thermal-printer/pics/VT_Codex_Thermique_F.png' , 'G':'/home/pi/git/py-thermal-printer/pics/VT_Codex_Thermique_G.png' , 'H':'/home/pi/git/py-thermal-printer/pics/VT_Codex_Thermique_H.png' , 'I':'/home/pi/git/py-thermal-printer/pics/VT_Codex_Thermique_I.png' , 'J':'/home/pi/git/py-thermal-printer/pics/VT_Codex_Thermique_J.png' , 'K':'/home/pi/git/py-thermal-printer/pics/VT_Codex_Thermique_K.png' , 'L':'/home/pi/git/py-thermal-printer/pics/VT_Codex_Thermique_L.png' , 'M':'/home/pi/git/py-thermal-printer/pics/VT_Codex_Thermique_M.png' , 'N':'/home/pi/git/py-thermal-printer/pics/VT_Codex_Thermique_N.png' , 'O':'/home/pi/git/py-thermal-printer/pics/VT_Codex_Thermique_O.png' , 'P':'/home/pi/git/py-thermal-printer/pics/VT_Codex_Thermique_P.png' , 'Q':'/home/pi/git/py-thermal-printer/pics/VT_Codex_Thermique_Q.png' , 'R':'/home/pi/git/py-thermal-printer/pics/VT_Codex_Thermique_R.png' , 'S':'/home/pi/git/py-thermal-printer/pics/VT_Codex_Thermique_S.png' , 'T':'/home/pi/git/py-thermal-printer/pics/VT_Codex_Thermique_T.png' , 'U':'/home/pi/git/py-thermal-printer/pics/VT_Codex_Thermique_U.png' , 'V':'/home/pi/git/py-thermal-printer/pics/VT_Codex_Thermique_V.png' , 'W':'/home/pi/git/py-thermal-printer/pics/VT_Codex_Thermique_W.png' , 'X':'/home/pi/git/py-thermal-printer/pics/VT_Codex_Thermique_X.png' , 'Y':'/home/pi/git/py-thermal-printer/pics/VT_Codex_Thermique_Y.png' , 'Z':'/home/pi/git/py-thermal-printer/pics/VT_Codex_Thermique_Z.png' , '0':'/home/pi/git/py-thermal-printer/pics/VT_Codex_Thermique_0.png' , '1':'/home/pi/git/py-thermal-printer/pics/VT_Codex_Thermique_1.png' , '2':'/home/pi/git/py-thermal-printer/pics/VT_Codex_Thermique_2.png' , '3':'/home/pi/git/py-thermal-printer/pics/VT_Codex_Thermique_3.png' , '4':'/home/pi/git/py-thermal-printer/pics/VT_Codex_Thermique_4.png' , '5':'/home/pi/git/py-thermal-printer/pics/VT_Codex_Thermique_5.png' , '6':'/home/pi/git/py-thermal-printer/pics/VT_Codex_Thermique_6.png' , '7':'/home/pi/git/py-thermal-printer/pics/VT_Codex_Thermique_7.png' , '8':'/home/pi/git/py-thermal-printer/pics/VT_Codex_Thermique_8.png' , '9':'/home/pi/git/py-thermal-printer/pics/VT_Codex_Thermique_9.png' , ' ':'/home/pi/git/py-thermal-printer/pics/VT_Codex_Thermique_00_Espace.png' } ch='' Cnt=0; while ch != '&': ch = getKey() if(('\n' == ch) or ('\r' == ch)): Cnt = 0 # return char won't print elif(0 == Cnt): Cnt = 1 # will print, next will not else: Cnt = 0 ch = '\n' # won't print next will print if ch in ImgArrays : ImageName = ImgArrays[ch] if os.path.isfile(ImageName): # print('for %s printing %s' % (ch, ImageName)) PrintImage( ImageName); # else : # print('for %s do not find %s' % (ch, ImageName)) # else : # print(' no pics defined for %s ... sniff'% ch) ** -> dans le clavier en kapla, un arduino Leonardo:** #include #include // Which pin on the Arduino is connected to the NeoPixels? // On a Trinket or Gemma we suggest changing this to 1 #define PIN 13 // How many NeoPixels are attached to the Arduino? #define NUMPIXELS 14 // When we setup the NeoPixel library, we tell it how many pixels, and which pin to use to send signals. // Note that for older NeoPixel strips you might need to change the third parameter--see the strandtest // example for more information on possible values. Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800); int delayval = 13500; // delay for half a second const int spacePin = 12; // the number of the pushbutton pin int buttonPressed = 0; // variable for reading the pushbutton status int printing = 0; const byte ROWS = 6; //four rows const byte COLS = 6; //three columns char keys[ROWS][COLS] = { {'0', '1', '2', '3', '4', '5'}, {'A', 'B', 'C', 'D', 'E', 'F'}, {'N', 'O', 'P', 'Q', 'R', 'S'}, {'T', 'U', 'V', 'W', 'X', 'Y'}, {'G', 'H', 'I', 'J', 'K', 'L'}, {'6', '7', '8', '9', 'M', 'Z'} }; byte rowPins[ROWS] = {6, 7, 8, 9, 10, 11}; //connect to the row pinouts of the keypad byte colPins[COLS] = {0, 1, 2, 3, 4, 5}; //connect to the column pinouts of the keypad Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS ); void setup() { pixels.begin(); // This initializes the NeoPixel library. Keyboard.begin(); pinMode(spacePin, INPUT_PULLUP); //Serial.begin(9600); } void loop() { if (printing == 0) { lightsGreen(); int sensorVal = digitalRead(12); if (sensorVal == LOW && buttonPressed == 0) { buttonPressed = 1; //Serial.println("SPACE"); Keyboard.write(0x20); Keyboard.releaseAll(); printing = 1; lightsRed(); delay(delayval); // Delay for a period of time (in milliseconds). Keyboard.write(0x20); Keyboard.releaseAll(); delay(200); Keyboard.write(0xB0); Keyboard.releaseAll(); delay(200); printing = 0; } else if (sensorVal == HIGH && buttonPressed == 1) { buttonPressed = 0; } char key = keypad.getKey(); if (key) { //Serial.println(key); Keyboard.write(key); Keyboard.releaseAll(); printing = 1; lightsRed(); delay(delayval); // Delay for a period of time (in milliseconds). Keyboard.write(key); Keyboard.releaseAll(); delay(200); Keyboard.write(0xB0); Keyboard.releaseAll(); delay(200); printing = 0; } } } void lightsGreen() { for (int i = 0; i < NUMPIXELS; i++) { // pixels.Color takes RGB values, from 0,0,0 up to 255,255,255 pixels.setPixelColor(i, pixels.Color(0, 255, 0)); // Moderately bright green color. pixels.show(); // This sends the updated pixel color to the hardware. } } void lightsRed() { for (int i = 0; i < NUMPIXELS; i++) { // pixels.Color takes RGB values, from 0,0,0 up to 255,255,255 pixels.setPixelColor(i, pixels.Color(255, 0, 0)); // Moderately bright green color. pixels.show(); // This sends the updated pixel color to the hardware. } }