Mai 27 2015

DevDocs – Un site pour toutes les docs de dev

DevDocs est un site qui regroupe toutes les docs dont on a toujours besoin… DOM, JS, Python, Django, Git, etc

Accessible en offline, integrable en tant que search provider, etc… Que demander de plus ?

http://devdocs.io/.

Avr 09 2013

Arduino et VFD Futaba

Olimex OLIMEXINO-32U4

Olimex OLIMEXINO-32U4

I just bought an Arduino. Or to be more specific, an enhanced clone of the Arduino Leonardo : the Olimex OLIMEXINO-32U4. This board features, unlike the original Leonardo board, a better power that allows it to run on 5v, 3.3v, or on li-ion battery. It also sports two leds, a button and an extension port.

I plan to use it with a bluetooth module that runs only on 3.3v, so the ability to power this arduino on 3.3v will ease it !

For my start project, I want to try to use an old Futaba GP1006C02 VFD graphic display. It’s a mid 90’s part that I collected in a garbage and that was lying on a shelf since. I wondered if I could use it to make a status display for my home server.

Hardware:

My workbench

My workbench

The VFD is addressed like a RAM. So I had to use a lot of I/O to handle it. 13 address lines, 8 data bits and 4 control lines. Problem, I only have 23 I/O available on the arduino (20 on the headers and 3 more on the extension port or the ICSP header). The ATMega 32U4 normally provides 26 I/O but two are used by the arduino firmware to drive the USB RX/TX leds and the last one is used by the button. I don’t want to modify the Arduino board and software to use them so I’ll had to find a way to wire everything.

First obvious trick, the /CS and /MERQ signals can be driven by the same I/O according the VFD datasheet. Second trick, I don’t need read access to the display memory, so I can wire /RD to 5v. But I still need 2 I/O. So I decide to use a binary counter to generate the address. I find in a drawer a good old 4029 4 bits binary/décimal counter/decounter. With just a reset and pulse signals, I can drive 4 address bits.

Now, I want an easy way to wire everything. I don’t want to make a custom PCB and just use a stripboard. To simplify wiring, I decided to not try to wire everything in an ordered way. The software will have to convert addresses and datas to the right ports and I/O. But I found a way to not shuffle things too much. I managed to stick the datas to the 4 higher bits of ports B and F, and the address to the 7 available bits of port D and the 4 bits of the counter. I decided to put the 4 bits of the counter to A3 through A7. This can seem odd, but when you look at the way the addresses map to pixels in the VFD datasheet, mapping at this address allow me to push 16 continuous columns of 8 pixels. The display will be sliced in 8 rows of 16 blocks of 16×8 pixels.

The control bits (/CS, /WR, CP and PL) are wired to nearest I/O available. Thank to the use of the counter, I have 2 I/O available. Maybe I could wire the /RD signal after all 😉

vfd

Not too messy for so many I/O! 13040027

Not too messy for so many I/O!

Testing the counter !

Testing the counter !

Arduino software :

There’s some small challenges with the software. The first one is to map a data byte to the 4 higher bits of the port B and F. Odd bits go to port F and pair bits go to port B.

There maybe an optimized way to do it, but the only way I find was to do it bit by bit. Mask the bit, shift it to it’s position, and repeat… Performance is not really an issue actually, so I’ll stick with this…

1
2
3
4
5
6
7
8
9
10
11
12
13
void setData(byte d) {
    PORTB = (PORTB & 0x0F) | ((
            ((d & 0x40) << 1) |
            ((d & 0x10) << 2) |
            ((d & 0x04) << 3) |
            ((d & 0x01) << 4)) & 0xF0);

    PORTF = (PORTF & 0x0F) | ((
            ((d & 0x80)     ) |
            ((d & 0x20) << 1) |
            ((d & 0x08) << 2) |
            ((d & 0x02) << 3)) & 0xF0);
}

The second challenge is to convert row and column to a valid address on port B. I decided to use two maps for the 8 rows and 16 columns.

1
2
3
4
5
6
7
void setAddress(byte l, byte c) {
    byte lmap[] = { 0x00, 0x08, 0x04, 0x0C, 0x02, 0x0A, 0x06, 0x0E };
    byte cmap[] = { 0x00, 0x01, 0x40, 0x41, 0x10, 0x11, 0x50, 0x51,
                    0x80, 0x81, 0xC0, 0xC1, 0x90, 0x91, 0xD0, 0xD1 };

    PORTD = (PORTD & 0x20) | lmap[l] | cmap[c];
}

The rest of the code is easy to understand, I won’t detail it. It allows me to send bitmaps and controls commands over USB.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
#define DELAY_62ns5 __asm__("nop\n\t")
#define SET(_port, _bit) _port |= 1 << _bit
#define UNSET(_port, _bit) _port &= ~(1 << _bit)

void setData(byte d) {
    PORTB = (PORTB & 0x0F) | ((
            ((d & 0x40) << 1) |
            ((d & 0x10) << 2) |
            ((d & 0x04) << 3) |
            ((d & 0x01) << 4)) & 0xF0);

    PORTF = (PORTF & 0x0F) | ((
            ((d & 0x80)     ) |
            ((d & 0x20) << 1) |
            ((d & 0x08) << 2) |
            ((d & 0x02) << 3)) & 0xF0);
}

void setAddress(byte l, byte c) {
    byte lmap[] = { 0x00, 0x08, 0x04, 0x0C, 0x02, 0x0A, 0x06, 0x0E };
    byte cmap[] = { 0x00, 0x01, 0x40, 0x41, 0x10, 0x11, 0x50, 0x51,
                    0x80, 0x81, 0xC0, 0xC1, 0x90, 0x91, 0xD0, 0xD1 };

    PORTD = (PORTD & 0x20) | lmap[l] | cmap[c];
}

void resetBlock() {
    PORTF |= 0x01;
    DELAY_62ns5;
    PORTF &= ~0x01;
    DELAY_62ns5;
}

void incBlock() {
    PORTF |= 0x02;
    DELAY_62ns5;
    PORTF &= ~0x02;
    DELAY_62ns5;
}

void delayNs() {
}

void write() {
    PORTC &= ~0x40; // unset /CS
    DELAY_62ns5; // delay > 200ns
    DELAY_62ns5;
    DELAY_62ns5;
    PORTC &= ~0x80; // unset /WR
    DELAY_62ns5; // delay > 100nx
    PORTC |= 0xC0; // set /CS and /WR
}

char page_displayed = 0;
char page_writed = 0;

void setPage() {
    PORTB |= 0x02; // set A12
    setData((page_displayed & 0x03) | ((page_writed << 2) & 0x0C));
    write();
    PORTB &= ~0x02; // unset A12
}

void showPage(byte page) {
    page_displayed = page;
    setPage();
}

void setWritePage(byte page) {
    page_writed = page;
    setPage();
}

void setLuminosity(byte page) {
    PORTB |= 0x02; // set A12
    PORTE |= 0x40; // set A11
    page &= 0x0F;
    if(page && page < 0x06)
        page = 0x06;
    setData(page);
    write();
    PORTB &= ~0x02; // unset A12
    PORTE &= ~0x40; // unset A11
}

void setup() {
    byte c, l, i, j;
    // all ports are outputs
    DDRB = DDRC = DDRD = DDRE = DDRF = 0xFF;
    PORTC &= ~0x40; // unset /CS
    PORTC |= 0x80; // set /WR
    resetBlock();

    setPage();
    for(l = 0; l < 8; l++) {
        for(c = 0; c < 16; c++) {
            setAddress(l, c);
            i = 16;
            while(i) {
                incBlock();
                setData(0x00);
                write();
                i--;
            }
        }
    }
    setLuminosity(15);

    Serial.begin(115200);
}

char anim = 0;
void loop() {
    char i, l, c;
    byte code;

    if(Serial.available() > 0) {
        code = Serial.read();

        switch(code & 0xF0) {
            case 0x80:
                setWritePage(code & 0x0F);
                break;
            case 0x90:
                showPage(code & 0x0F);
                break;
            case 0xA0:
                setLuminosity(code & 0x0F);
                break;
            case 0xB0: // Blank
                for(l = 0; l < 8; l++) {
                    for(c = 0; c < 16; c++) {
                        setAddress(l, c);
                        i = 16;
                        while(i) {
                            incBlock();
                            setData(0x00);
                            write();
                            i--;
                        }
                    }
                }
                break;
            case 0xC0: // Full on
                for(l = 0; l < 8; l++) {
                    for(c = 0; c < 16; c++) {
                        setAddress(l, c);
                        i = 16;
                        while(i) {
                            incBlock();
                            setData(0xFF);
                            write();
                            i--;
                        }
                    }
                }                    break;
            case 0xD0: // Test patern
                for(l = 0; l < 8; l++) {
                    for(c = 0; c < 16; c++) {
                        setAddress(l, c);
                        i = 16;
                        while(i) {
                            i--;
                            incBlock();
                            setData(i < 8 ? (1 << i) : (0x80 >> (i - 8)));
                            write();
                        }
                    }
                }                    break;
            case 0xE0: // Start stop animation patern
                anim = code & 1 ? 8 : 0;
                break;
            case 0xF0: // Light/Clear dot
                anim = 0;
                // TODO
                // Need hardware modification for read function
                break;
            default:
                // address followed by 16 bytes of data
                setAddress((code & 0x70) >> 4, code & 0x0f);
                i = 16;
                while(i) {
                    while(Serial.available() <= 0) {}
                    setData(Serial.read());
                    write();
                    incBlock();
                    i--;
                }
        }
    }

    if(anim & 0x8) {
        for(l = 0; l < 8; l++) {
            for(c = 0; c < 16; c++) {
                setAddress(l, c);
                i = 16;
                while(i) {
                    i--;
                    incBlock();
                    setData(1 << ((i + anim) & 0x07));
                    write();
                }
            }
        }
        anim++;
        if(anim & 0x10)
            anim = 0x08;
    }

}

The software takes commands over USB serial link. The first byte is the command byte. If the higher bit is 0, then bits 6 to 4 encode the row, and bits 3 to 0 encode the column. The control byte is then followed by 16 data bytes that correspond to the 16 columns of the addressed block.

 

If the first bit is 0, then the commands are :

Bit Command
7 6 5 4 3 2 1 0
1 0 0 0 0 0 page Read / Write page
1 0 0 1 0 0 page Displayed page
1 0 1 0 luminosity Set luminosity
1 0 1 1 0 0 0 0 Blank
1 1 0 0 0 0 0 0 Full on
1 1 0 1 0 0 0 0 Test Pattern (/\/\/\/\)
1 1 1 0 0 0 0 0 Test animation (sliding ////)
1 1 1 1 0 0 0 0 Not used

 

The test pattern (command 0xd0)

The test pattern (command 0xd0)

Computer software:

I wrote a small python script that sends an hard coded image to the VFD :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
import serial, pprint

a = """                                                                                                                                                                                                                                                                
                                                                                                                                                                                                                                                               
                                                  XXX                                                                                        XXX                                        XXXXXXXXX                                                              
                                                XXXXXXX                           XXXXXX             XXX                                   XXXXXXX                                    XXXXXXXXXXXXX                             XXXX                            
                    XXX                         XXXXXXX                         XXXXXXXXXX         XXXXXXX                                 XXXXXXX                                    XXXXXXXXXXXXXX                          XXXXXXXX                          
                  XXXXXXX                      XXXXXXXXX                     XXXXXXXXXXXXX         XXXXXXXX                               XXXXXXXXX                                  XXXXXXXXXXXXXXXXXX                       XXXXXXXXX                        
                  XXXXXXX                      XXXXXXXXX                   XXXXXXXXXXXXXXXX       XXXXXXXXXX                              XXXXXXXXX                                  XXXXXXXXXXXXXXXXXXXX                    XXXXXXXXXX                        
                 XXXXXXXXX                     XXXXXXXXX                 XXXXXXXXXXXXXXXXXX       XXXXXXXXXX                              XXXXXXXXXX                                 XXXXXXXXXXXXXXXXXXXXX                   XXXXXXXXXXXX                      
                 XXXXXXXXX                     XXXXXXXXX                XXXXXXXXXXXXXXXXXXX       XXXXXXXXXXX                             XXXXXXXXXX                                XXXXXXXXXXXXXXXXXXXXXX                   XXXXXXXXXXXX                      
                 XXXXXXXXXX                    XXXXXXXXX               XXXXXXXXXXXXXXXXXXX         XXXXXXXXXX                              XXXXXXXXX                                XXXXXXXXXXXXXXXXXXXXXXX                  XXXXXXXXXXXXX                      
                 XXXXXXXXXX                   XXXXXXXXXX               XXXXXXXXXXXXXXXXXXX         XXXXXXXXXXX                             XXXXXXXXX                                XXXXXXXXXXXXXXXXXXXXXXX                  XXXXXXXXXXXXX                      
                  XXXXXXXXXX                  XXXXXXXXXX              XXXXXXXXXXXXXXXXXX            XXXXXXXXXXX                            XXXXXXXXX                               XXXXXXXXXXXXXXXXXXXXXXXXX                XXXXXXXXXXXXXXX                    
                  XXXXXXXXXX                  XXXXXXXXXX             XXXXXXXXXXXXXXXX               XXXXXXXXXXX                            XXXXXXXXX                               XXXXXXXXXXXXXXXXXXXXXXXXXX               XXXXXXXXXXXXXXX                    
                   XXXXXXXXX                  XXXXXXXXX              XXXXXXXXXXXXX                   XXXXXXXXXXX                           XXXXXXXXX                               XXXXXXXXXXXXXXXXXXXXXXXXXX               XXXXXXXXXXXXXXXX                    
                   XXXXXXXXX                 XXXXXXXXXX             XXXXXXXXXXX                       XXXXXXXXXX                           XXXXXXXXXX                              XXXXXXXXXXXX   XXXXXXXXXXXX              XXXXXXXXXXXXXXXX                    
                   XXXXXXXXX                 XXXXXXXXXX             XXXXXXXXXXX                       XXXXXXXXXXX                          XXXXXXXXXX                              XXXXXXXXXX       XXXXXXXXXX              XXXXXXXXXXXXXXXXX                  
                   XXXXXXXXX                 XXXXXXXXX             XXXXXXXXXXX                         XXXXXXXXXX                           XXXXXXXXX                              XXXXXXXXXX       XXXXXXXXXX              XXXXXXXXXXXXXXXXX                  
                   XXXXXXXXXX                XXXXXXXXX             XXXXXXXXXX                          XXXXXXXXXX                           XXXXXXXXX                              XXXXXXXXX         XXXXXXXXXX             XXXXXXXXXXXXXXXXX                  
                   XXXXXXXXXX                XXXXXXXXX             XXXXXXXXXX                           XXXXXXXXXX                          XXXXXXXXX                              XXXXXXXXX         XXXXXXXXXX              XXXXXXXXXXXXXXXX                  
                    XXXXXXXXX                XXXXXXXXX            XXXXXXXXXX                            XXXXXXXXXX                          XXXXXXXXX                              XXXXXXXXX         XXXXXXXXXXX             XXXXXXXXXXXXXXXX                  
                    XXXXXXXXXX               XXXXXXXXX            XXXXXXXXXX                            XXXXXXXXXX                          XXXXXXXXX                              XXXXXXXXX          XXXXXXXXXX              XXXXXXXXXXXXXXX                  
                    XXXXXXXXXX               XXXXXXXXX            XXXXXXXXXX                             XXXXXXXXX                          XXXXXXXXXX                             XXXXXXXXX          XXXXXXXXXX              XXXXXXXXXXXXXXXX                  
                     XXXXXXXXX               XXXXXXXXX            XXXXXXXXX                              XXXXXXXXX                          XXXXXXXXXX                             XXXXXXXXX           XXXXXXXXX               XXXXXXXXXXXXXXX                  
                     XXXXXXXXX               XXXXXXXXX            XXXXXXXXX                              XXXXXXXXX                          XXXXXXXXXX                             XXXXXXXXX           XXXXXXXXX                XXXXXXXXXXXXXX                  
                     XXXXXXXXXX              XXXXXXXXX            XXXXXXXXX                              XXXXXXXXX                           XXXXXXXXX                             XXXXXXXXXX          XXXXXXXXXX               XXXXXXXXXXXXXXX                
                     XXXXXXXXXX              XXXXXXXXX            XXXXXXXXX                              XXXXXXXXX                           XXXXXXXXX                             XXXXXXXXXX          XXXXXXXXXX                XXXXXXXXXXXXXX                
                      XXXXXXXXX              XXXXXXXXX            XXXXXXXXX      XXXXXXX                 XXXXXXXXXX                          XXXXXXXXX                             XXXXXXXXXX          XXXXXXXXXX                XXXXXXXXXXXXXX                
                      XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX           XXXXXXXXXXXXXXXXXXXXXXXX               XXXXXXXXXX                          XXXXXXXXX                              XXXXXXXXX           XXXXXXXXX                XXXXXXXXXXXXXX                
                      XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX           XXXXXXXXXXXXXXXXXXXXXXXX               XXXXXXXXXX                          XXXXXXXXX                              XXXXXXXXX           XXXXXXXXX                XXXXXXXXXXXXXXX                
                      XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX           XXXXXXXXXXXXXXXXXXXXXXXXX               XXXXXXXXX                          XXXXXXXXX                              XXXXXXXXX           XXXXXXXXX                 XXXXXXXXXXXXXX                
                      XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX           XXXXXXXXXXXXXXXXXXXXXXXXX               XXXXXXXXX                          XXXXXXXXX                              XXXXXXXXXX          XXXXXXXXX                 XXXXXXXXXXXXXX                
                      XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX           XXXXXXXXXXXXXXXXXXXXXXXXX               XXXXXXXXX                          XXXXXXXXX                              XXXXXXXXXX          XXXXXXXXX                 XXXXXXXXXXXXXX                
                      XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX           XXXXXXXXXXXXXXXXXXXXXXXX                XXXXXXXXX                          XXXXXXXXX                               XXXXXXXXX          XXXXXXXXX                  XXXXXXXXXXXXX                
                       XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX           XXXXXXXXXXXXXXXXXXXXXXXX                XXXXXXXXX                          XXXXXXXXX                               XXXXXXXXX          XXXXXXXXX                  XXXXXXXXXXXXX                
                       XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX           XXXXXXXXXXXXXXXXXXXXXX                  XXXXXXXXX                          XXXXXXXXXX                              XXXXXXXXX          XXXXXXXXX                   XXXXXXXXXXXX                
                       XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX           XXXXXXXXXXXXXXXXXX                      XXXXXXXXX                          XXXXXXXXXX                              XXXXXXXXX          XXXXXXXXX                   XXXXXXXXXXXX                
                       XXXXXXXXX              XXXXXXXXX           XXXXXXXXXX                              XXXXXXXXX                           XXXXXXXXX                               XXXXXXXX          XXXXXXXXX                   XXXXXXXXXXXX                
                       XXXXXXXXX              XXXXXXXXX            XXXXXXXXX                              XXXXXXXXX                           XXXXXXXXX                               XXXXXXXX           XXXXXXXX                    XXXXXXXXXXX                
                       XXXXXXXXX              XXXXXXXXXX           XXXXXXXXXX                             XXXXXXXXX                           XXXXXXXXX                               XXXXXXXX           XXXXXXXX                    XXXXXXXXXXX                
                       XXXXXXXXX              XXXXXXXXXX           XXXXXXXXXX                            XXXXXXXXXX                           XXXXXXXXX                                XXXXXXXX          XXXXXXXX                     XXXXXXXXXXX              
                      XXXXXXXXXX              XXXXXXXXXX           XXXXXXXXXXX                           XXXXXXXXXX                           XXXXXXXXX                                XXXXXXXX          XXXXXXXX                     XXXXXXXXXXX              
                      XXXXXXXXXX               XXXXXXXXX            XXXXXXXXXX                           XXXXXXXXXX                          XXXXXXXXXX                                XXXXXXXXX        XXXXXXXXX                      XXXXXXXXXX              
                      XXXXXXXXXX               XXXXXXXXXX           XXXXXXXXXXX                          XXXXXXXXX                           XXXXXXXXXX                                XXXXXXXXX        XXXXXXXXX                       XXXXXXXXX              
                      XXXXXXXXX                XXXXXXXXXX            XXXXXXXXXX                          XXXXXXXXX                           XXXXXXXXXX                                XXXXXXXXX       XXXXXXXXXX                       XXXXXXXXX              
                      XXXXXXXXX                 XXXXXXXXX             XXXXXXXXX           XXXX           XXXXXXXXX                           XXXXXXXXXX                                 XXXXXXXX       XXXXXXXXXX                       XXXXXXXXX              
                      XXXXXXXXX                 XXXXXXXXX             XXXXXXXXX         XXXXXXXX         XXXXXXXXX                           XXXXXXXXXX                                 XXXXXXXX       XXXXXXXXXX                       XXXXXXXXX              
                     XXXXXXXXXX                 XXXXXXXXXX            XXXXXXXXXX       XXXXXXXXX         XXXXXXXXX                           XXXXXXXXXX                                 XXXXXXXXX     XXXXXXXXXX                         XXXXXXX                
                     XXXXXXXXXX                 XXXXXXXXXX            XXXXXXXXXX      XXXXXXXXXXX       XXXXXXXXXX                           XXXXXXXXX                   XXXX           XXXXXXXXX     XXXXXXXXXX                         XXXXXXX                
                    XXXXXXXXXXX                 XXXXXXXXXX            XXXXXXXXXXX    XXXXXXXXXXXX       XXXXXXXXXX   XXX         XXX         XXXXXXXXX                XXXXXXXXX          XXXXXXXXXX   XXXXXXXXXX                           XXX                  
                    XXXXXXXXXX                   XXXXXXXXX             XXXXXXXXXXXX XXXXXXXXXXXXX      XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX       XXXXXXXXXX       XXXXXXXXXXXXXXXXX          XXXXXXXXXXXXXXXXXXXXXX                                                
                   XXXXXXXXXXX                   XXXXXXXXXX            XXXXXXXXXXXXXXXXXXXXXXXXX       XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX      XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX          XXXXXXXXXXXXXXXXXXXXX                               XXX              
                   XXXXXXXXXX                    XXXXXXXXXX            XXXXXXXXXXXXXXXXXXXXXXXXX       XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX       XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX          XXXXXXXXXXXXXXXXXXXX                              XXXXXXX            
                  XXXXXXXXXXX                    XXXXXXXXXX             XXXXXXXXXXXXXXXXXXXXXXX        XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX      XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX           XXXXXXXXXXXXXXXXXXX                             XXXXXXXXX            
                  XXXXXXXXXX                      XXXXXXXXX             XXXXXXXXXXXXXXXXXXXXXX          XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX      XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX             XXXXXXXXXXXXXXXXX                              XXXXXXXXX            
                 XXXXXXXXXXX                      XXXXXXXXX              XXXXXXXXXXXXXXXXXXXX           XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX      XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX             XXXXXXXXXXXXXXXXX                             XXXXXXXXXXX          
                 XXXXXXXXXX                       XXXXXXXXX               XXXXXXXXXXXXXXXXXX            XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX        XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX               XXXXXXXXXXXXXXX                              XXXXXXXXXXX          
                 XXXXXXXXXX                        XXXXXXX                  XXXXXXXXXXXXXXX              XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX        XXXXXXXXXXXXXXXXXXXXXXXXXXXXX                  XXXXXXXXXXXXXX                              XXXXXXXXXXX        X X
                  XXXXXXXX                         XXXXXXX                    XXXXXXXXXXX                XXXXXXXXXXXXXXXXXXXXXXXXXXXX           XXXXXXXXXXXXXXXXXXXXXXXXX                     XXXXXXXXXXXXX                               XXXXXXXXXXX         X
                  XXXXXXXX                           XXX                        XXXXXXX                    XXXXXXXXX                              XXXXXXXXXXXXXXXXXX                            XXXXXXXXXX                                XXXXXXXXXXX        X X
                    XXXX                                                                                                                                                                          XXXXXX                                  XXXXXXXXXXX         X
                                                                                                                                                                                                                                           XXXXXXXXX         X X
                                                                                                                                                                                                                                           XXXXXXXXX          X
                                                                                                                                                                                                                                             XXXXX           X X
                                                                                                                                                                                                                                                              X """



try:
    ser = serial.Serial('/dev/ttyACM0', 115200)
except Exception:
    ser = serial.Serial('/dev/ttyACM1', 115200)
   
a=a.replace('\n', '')

def block(c, l):
    s = chr(l * 16 + c)
    for i in range(16):
        b = 0;
        for j in range(8):
            offset = c * 16 + i + 256 * (l * 8 + j)
            # print 'c=%d, l=%d, i=%d, j=%d, c * 16 + i + 257 * (l * 8 + j)=%d, b=%02x, %s' % (c,l,i,j,offset, b, a[offset])
            if a[offset] != ' ':
                b |= 2**j
        s += chr(b)
    return s
if 1:
    for c in range (16):
        for l in range(8):
            s = block(c, l)
            #print ' '.join('%02x' % ord(l) for l in s)
            ser.write(s)
print ' '.join('%02x' % ord(l) for l in block(15,7))
print len(a), len(a) / 64, len(a) / 256

Conclusion:

The python script send this image to the VFD !

The python script send this image to the VFD !

I’ve had fun playing with this, but unfortunately I think I won’t use this VFD as a side display for my home server. First reason is that it is really greedy. It use ~2A @ 5v, that’s 10W. I can use a small color LCD that will be nicer and more power friendly ! And the second reason is that it make high pitched noise.

Avr 01 2013

CodinGame de Janvier

J’ai participé au CodinGame de Janvier. Contrairement à ceux de Mars, le niveau des exercices était vraiment sympa… Voici mes résultats, je m’en suis mieux sorti qu’au mois de mars avec une 29e place, 79% de réussite en 2h30 (sur 5h).

Je vais parler du 3e exercice. L’objectif était d’analyser des messages écrits en morse, mais sans séparateur entre les mots. Il fallait trouver le nombre de combinaisons de mots possible qui pourrait donner ce message. Le programme prenant en entrée le message, ainsi que la liste des mots possibles.

Première tentative

J’ai décidé de procéder récursivement. Je commence déjà par convertir tous les mots de mon dictionnaires en morse.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import sys
codex = {
    'A': '.-', 'B': '-...', 'C': '-.-.', 'D': '-..', 'E': '.',
    'F': '..-.', 'G': '--.', 'H': '....', 'I': '..', 'J': '.---',
    'K': '-.-', 'L': '.-..', 'M': '--', 'N': '-.', 'O': '---',
    'P': '.--.', 'Q': '--.-', 'R': '.-.', 'S': '...', 'T': '-',
    'U': '..-', 'V': '...-', 'W': '.--', 'X': '-..-', 'Y': '-.--', 'Z': '--..',
}
def encode(m):
    return ''.join([codex[i] for i in m])

morse = raw_input()
n = int(raw_input())

mots = []
for i in range(n):
    m = raw_input()
    mots.append(encode(m))

Ma fonction récursive commence par comparer le début de ma chaine avec tous les mots. Si une occurrence est trouvée, je rappel la fonction avec le reste de la chaine. La fonction retourne le nombre de possibilités trouvées. Ma condition de fin de récursivité est quand la chaine à traduire est vide.

Voici le code :

1
2
3
4
5
6
7
8
9
10
def _try(s):
    if not s:
        return 1
    c = 0
    for m in mots:
        if m == s[:len(m)]:
            c += _try(s[len(m):])
    return c

print _try(morse)

Première optimisation

Le problème avec les fonctions récursives, c’est souvent le temps d’exécution. Ici, c’était juste désastreux. La première optimisation que je vois, c’est dans ma boucle qui test les mots. J’ai décidé d’essayer de ne couper ma chaine qu’une seule fois pour chaque longueur de mot. Puis d’essayer de trouver cette chaine dans un dictionnaire. J’ai parier sur le fait que la recherche dans un dictionnaire est très optimisé.

Je change donc ma routine de génération du dictionnaire :

1
2
3
4
5
6
7
8
lmots = 0
mots = {}
for i in range(n):
    m = raw_input()
    m2 = encode(m)
    if len(m2) > lmots:
        lmots = len(m2)
    mots[m2] = m

Et ma routine de recherche :

1
2
3
4
5
6
7
8
9
def _try(s):
    if not s:
        return 1
    c = 0
    for i in range(min(lmots, len(s))):
        m = s[:i+1]
        if mots.has_key(m):
            c += _try(s[i+1:])
    return c

Ainsi, dans la boucle qui est dans la fonction de recherche, je passe d’une boucle qui fait la longueur des mots de mon dictionnaire, à une boucle dont la longueur est égale au mot le plus long de mon dictionnaire. Sur un gros dictionnaire, ça peut faire une sacré économie. Mais ce n’est pas encore vraiment ça. En particulier le test 4 met une éternité… Comme j’en avais un peu marre, c’est le code que j’ai soumis au bout de 52 minutes.

Le bug

Mais je me suis planté. J’avais introduit un bug en utilisant un dictionnaire. Le bug est pourtant évident, c’est même le sujet de l’épreuve. Une suite de . et – en morse peut correspondre à plusieurs suites de lettres différentes. Et en créant mon dictionnaire, comme j’utilise le code morse comme clé, si plusieurs mots dans le dictionnaire donne la même clé, je n’enregistre que le dernier de ces mots. J’oublie donc des mots, et donc, des solutions.

Pour corriger ça, je décide donc de stocker le nombre de mots possibles pour chaque clé.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
lmots = 0
mots = {}
for j in range(n):
    m = encode(raw_input())
    lmots = max(len(m), lmots)
    mots[m] = mots.get(m, 0) + 1

def _try(s):
    if not s:
        return 1
    c = 0
    for i in range(min(lmots, len(s))):
        m = s[:i+1]
        if mots.has_key(m):
            c += mots[m] * _try(s[i+1:])
    return c

Le bug est corrigé, mais l’exécution est toujours terriblement lente !

Dernière optimisation

Il y a une optimisation évidente. On appel _try sur le reste de la chaine. Si par hasard, en trouvant un autre début de mot, on se retrouve avec la même fin de chaine, il n’y a aucun intérêt à recalculer toute la chaine. Vous l’avez compris, je parle ici de faire du cache !

Voici mon implémentation :

1
2
3
4
5
6
7
8
9
10
11
12
13
cache = {}
def _try(s):
    if len(s) in cache:
        return cache[len(s)]
    if not s:
        return 1
    c = 0
    for i in range(min(lmots, len(s))):
        m = s[:i+1]
        if mots.has_key(m):
            c += len(mots[m]) * _try(s[i+1:])
    cache[len(s)] = c
    return c

Et voilà ! Avec cette petite optimisation, j’arrive à exécuter le test 4 en moins d’une demi seconde. Contrat rempli, 100% des tests passent !

Mar 27 2013

CodinGame de mars

Hier, j’ai participé au concours CodinGame de mars. J’ai trouvé les exercices très simples par rapport à la version précédente. Cette fois, pas besoin de se prendre la tête pour trouver comment faire l’algorithme, pas besoin de se demander comment diminuer la complexité de ma récursion pour réussir à passer dans les critères de temps ou de mémoire. Ici, en lisant les premières lignes d’énoncé, je voyait déjà le code dans ma tête… Un peu décevant. Mais bon, vous allez voir que ça ne m’a pas empêché de me planter ;-).

Vous pouvez consulter les énoncés et mon code ici

Exercice 1

Le premier exercices est très simple, il faut calculer une suite.

1
2
3
4
5
6
5
1 5
1 1 1 5
3 1 1 5
1 3 2 1 1 5
1 1 1 3 1 2 2 1 1 5

Une ligne est donc composée des couples comportant le décompte des nombres similaires consécutifs et le nombre en question. Le programme, doit calculer la ligne N quand on lui donne la première ligne R et N.

J’ai décidé de ne pas me prendre la tête et de calculer séquentiellement toutes les itérations de la suite pour arriver à la ligne demandée. Il doit surement y avoir un moyen de déduire la ligne directement sans faire toutes les autres, mais bon vu les contraintes très larges et les plages de valeurs proposées, ça ne valait pas le coup. Voici mon code :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import sys

n = int(raw_input())
l = int(raw_input())
s = [ n ]
for i in xrange(l - 1):
    c = 0
    a = s[0]
    s2 = []
    for j in s:
        if a == j:
            c += 1
        elif c > 0:
            s2.append(c)
            s2.append(a)
            c = 1
        a = j
    if c > 0:
        s2.append(c)
        s2.append(a)

    s = s2

print ' '.join(['%d' % i for i in s])

Mon code à passé tous les tests avec succès. J’ai mis 16 minutes pour l’écrire.

Exercice 2

Pour l’exercice suivant, le programme doit prendre en entré un fichier structuré mais non formaté et le formater.

En lisant l’énoncé, on pense tout de suite qu’il faut écrire un parser. Mais en regardant de plus près, comme on veut juste formater, on peut simplement imprimer le code formaté au fur et à mesure que l’on avance dans la lecture du fichier, sans devoir construire une représentation du fichier complète en mémoire. Je suis donc parti sur l’écriture d’une petite machine à états.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
import sys, re

r = re.compile('\d')

n = int(raw_input())
s = ''
for i in range(n):
    s += raw_input() + '\n'



BLOCK = 1
STRING = 2
INT = 3

state = [BLOCK]
indent = ''
i = 0
accumulator = None
statement = ''
while i < len(s):
    #print s[i], state
    if state[-1] == BLOCK:
        if s[i] == '(':
            if statement:
                print indent + statement
                statement = ''
            print indent + '('
            indent += '    '
            state.append(BLOCK)
        elif s[i] == ')':
            # print last statement
            if statement:
                print indent + statement
                statement = ''
            del state[-1]
            indent = indent [: -4]
            statement = ')'
        elif s[i] == '=':
            statement += '='
        elif s[i] == ';':
            # print last statement
            print indent + statement + ';'
            statement = ''
        elif s[i] == "'":
            state.append(STRING)
            accumulator = ''
        elif s[i:i+4] == 'null':
            i += 3
            statement = 'null'
        elif s[i:i+4] == 'true':
            i += 3
            statement = 'true'
        elif s[i:i+5] == 'false':
            i += 4
            statement = 'false'
        elif r.match(s[i]):
            state.append(INT)
            accumulator = s[i]
        else:
            # junk
            pass
    elif state[-1] == STRING:
        if s[i] == "'":
            statement += "'" + accumulator + "'"
            del state[-1]
        else:
            accumulator += s[i]
    elif state[-1] == INT:
        if r.match(s[i]):
            accumulator += s[i]
        else:
            statement += accumulator
            del state[-1]
            i -= 1
    i += 1

print statement

Après quelques petits tests, j’ai soumis mon code au bout de 52 minutes. Malheureusement, j’ai fait une petite erreur. Je n’ai pas testé le cas où j’ai une CLE_VALEUR composée avec un true, false ou null. Dans ce cas, j’écrase la variable statement au lieu de concaténer… En gros il manque un + devant les = des lignes 50, 53 et 56 ! Du coup, mon code rate deux tests.

J’obtiens donc 95% de réussite en 1h08, ce qui me classe en 95e place… Si je n’avais pas fait cette étourderie, j’aurai été 8e… C’est con ! 😉

Bon, dès que j’ai retrouvé mes codes de ma participation au coding game de janvier, je fais faire un petit article. Là, les exercices étaient beaucoup plus intéressant !

Mar 21 2013

XKCD StackSort

Il y a quelques temps, Randall Munroe a publié sur sur webcomic, XKCD, un dessin sur des algorithmes de tri délirants… En survolant l’image avec la souris, il parle d’un autre algorithme : « stacksort ». Il est assez fun, c’est une fonction qui va chercher un algorithme de tri au hasard sur stackexchange à chaque itération, puis l’exécute et retourne le resultat…

Et bien, comme d’habitude avec XKCD, il y a quelqu’un pour mettre en pratique cette idée délirante :

http://gkoberger.github.com/stacksort/

Essayez le, mais sachez que cela va chercher et exécuter un bout de code au hasard sur le net… Même si le javascript de votre navigateur est dans un bac à sable, ce n’est peut être pas une bonne idée 😉

Mar 20 2013

Remplacer Google Reader

greader_killedGoogle Reader va fermer ! Depuis le temps que je voulais le faire, j’ai donc basculé sur NewsPipe.

C’est un client RSS qui envoi des mails. Comme ça, je règle le problème de la visualisation multi postes / clients, il suffit d’avoir de l’IMAP ou un webmail.

Il offre des possibilité intéressante comme :

  • pouvoir faire des mails de résumé pour certain flux (un mail par jour),
  • de télécharger et joindre les images dans le mail (pour une vrai lecture offline),
  • de remplacer le contenu de l’élément par une copie de la page web pointé (utile pour les flux qui ne contiennent qu’un résumé),
  • et le tout est simplement configurable avec un fichier OPML comme celui que j’ai récupéré sur Google Takeout ,
  • de filtrer les contenus avec des regex.

Il est écrit en python, ce qui m’a permit de facilement corriger un bug (les images avec des accents dans les URL n’était pas téléchargé) et ajouté une fonctionnalité permettant de ne prendre qu’une partie du HTML en utilisant un sélecteur CSS. Pour cela, j’ai utiliser l’excellente bibliothèque BeautifulSoup. J’ai soumis les deux patch à l’auteur de NewsPipe, mais les voici en téléchargement : newspipe-1.1.9-selector.diffnewspipe-1.1.9-bug_uri.diff

Il suffit de renseigner le newspipe.py avec le nom du fichier OPML et d’y ajouter le mail de destination. Pour ne pas poluer mon compte mail principal, j’ai créer un compte pour l’occasion. Enfin, il suffit de lancer newspipe.py. Pour cela j’ai créer l’unité suivante pour le lancer avec systemd : /etc/systemd/system/newspipe.service

1
2
3
4
5
6
7
8
9
[Unit]
Description=NewsPipe RSS to email
[Service]
Type=simple
User=puyb
WorkingDirectory=/home/puyb /bin/newspipe-1.1.9/
ExecStart=newspipe.py
[Install]
WantedBy=multi-user.target
1
2
# systemctl enable newspipe
# systemctl start newspipe

Voilà maintenant, j’ai un système auto-hébergé, avec plus de fonctionnalités que Google Reader. Elle est pas belle la vie ?;-)

Déc 28 2012

Replace xinetd with systemd

While reading the Samba ArchWiki page, I discovered that samba now include a web admin tool (SWAT). The ArchWiki propose to use xinet to start it… I remembered that systemd provide a socket management solution that can replace xinet (or inet)…

The mechanism behind xinet is that it’s configured to listen on a socket, and when a connection is received to the socket, it launch a program and bind its input and output to the socket stream…

To configure swat with systemd, you just have to create two new unit in /etc/systemd/system.
swat.socket

1
2
3
4
5
6
7
8
9
[Unit]
Description=SWAT Samba Web Admin Tool

[Socket]
ListenStream=127.0.0.1:901
Accept=true

[Install]
WantedBy=sockets.target

swat@.service

1
2
3
4
5
6
7
[Unit]
Description=SWAT Samba Web Admin Tool
After=local-fs.target

[Service]
ExecStart=/usr/sbin/swat
StandardInput=socket

Mind the @ at the end of the service unit… It tells systemd that the unit is started by the socket…

You can start the socket (and enable it, if you want this to be persistent).

1
systemctl start swat.socket

Then go to http://localhost:901/ and enjoy 😉

Tomorrow, I’ll try to submit a patch to the samba arch package maintainer…

Déc 12 2012

Résultats des Opens du Web

Nous avons obtenus un total de 61 points, répartis de la façon suivante :

  • Damien à gagné 22 points sur les deux épreuves de développement
  • Kévin à gagné 14 points sur les deux épreuves d’intégration et aucun point avec son logo dans l’épreuve de design
  • J’ai gagné 25 points sur les 3 épreuves d’administration.

Bravo à Kévin, qui s’est sacrifié sur les épreuves d’intégration et design pour nous laisser faire les épreuves d’administration et de développement. Ce n’est pas son métier, et je ne suis pas certain que j’aurais réussi à ramener 14 points sur ces épreuves 😉

Déc 07 2012

On a gagné les Opens du Web

Hier soir, Damien, Kevin et moi avons participé à la troisième édition des Open du Web à Paris. Et nous avons décroché la première place en équipe.

Les Opens du Web, c’est un concours dont les épreuves portent sur différents métiers du Web, à savoir programmation, intégration, design, administration, optimisation de référencement et gestion de communauté… Comme tous les trois, nous avons un profil de développeur, on pensait être mal partis. Les premières heures de l’épreuve nous ont confortés dans cette opinion. Et le dénouement a été une grande surprise…

Le concours avait lieu simultanément à Paris, Lille et Bordeaux. À Paris, il s’est déroulé dans les locaux de Sup’Internet. Et plus particulièrement, dans un parking souterrain digne des LAN de mon adolescence ;-). Ouvert aux quatre vents, il y règne un froid glacial, et la jolie demoiselle qui fait les présentations sur scène a bien du mal enthousiasmer la foule. Tristan Nitot dit quelques mots… Les participants s’installent, nous faisons connaissance avec les PC sous Ubuntu prêtés pour l’occasion… Quelques petits détails techniques réglés et nous pouvons attaquer ! Damien prend la programmation, Kévin, l’intégration, je prends l’administration…

La première épreuve est super simple : mettre en place une authentification par clés sur un serveur SSH. Je demande une machine virtuelle, deux minutes plus tard, je me logue. 30s plus tard, impossible de se reconnecter. Un organisateur vient et m’annonce que le serveur hôte est au bord du suicide, mais que les admins sont en train de lui remonter le moral, il faut patienter un peu. Quelques minutes plus tard, je peux enfin commencer à travailler. Première étape, je me crée des clefs pour pouvoir me connecter avec mon compte administrateur sur la machine. Je ne sais pas si c’est le stress du concours ou le froid, mais je me mélange les pinceaux et bloque au moins 30 minutes sur une erreur de débutant : le dossier .ssh n’avait pas le droit d’exécution ! Une fois cette bêtise réglée, je crée l’utilisateur demandé, copie la clé publique, tente de me connecter, mais rien : connexion impossible… J’active les logs de debug sur le client, puis sur le serveur. Et à force de fouiller, je me rend compte que la machine virtuelle fournie est configurée par défaut via PAM pour aller chercher les comptes SSH dans une base SQL distante. Je n’ai jamais vraiment bidouillé PAM, je tâtonne un peu. Une fois la config de PAM remise à plat, j’ai pu me connecter. Je désactive la connexion par mot de passe et valide mon épreuve. Ouf… Ça fait déjà plus d’une heure trente que je suis sur un exercice aussi trivial !

Pendant ce temps, mes collègues avaient fini depuis belle lurette leur première épreuve, mais un bug dans le système du concours les a empêché de passer à la seconde épreuve… Kévin qui avait fini en premier, pour ne pas perdre de temps, a eu le temps de valider la première étape de design avec un très joli logo. Damien m’a donné un coup de main pour résoudre mes soucis avec ma machine virtuelle… Nous voici donc prêt pour la deuxième épreuve… Il reste un tout petit peu plus d’une heure : c’est pas gagné…

Pour la seconde épreuve, il fallait configurer un pare-feu iptables ; facile ! Sauf que, si je le fais sur la machine virtuelle, je vais couper l’accès à ssh ?!? Après avoir demandé au jury si c’est bien ce qu’ils veulent, et une réponse à moitié satisfaisante, je décide de juste faire un script de configuration, libre à eux de l’exécuter pour se couper l’accès. Le voici :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/bin/bash

iptables -F
iptables -X

iptables -P INPUT DROP
#iptables -A INPUT -m conntrack --ctstate ESTABLISHED -j ACCEPT
#iptables -A INPUT -m state --state ESTABLISHED -j ACCEPT
iptables -I INPUT -p tcp -s 10.180.30.200 --dport 22 -j ACCEPT
iptables -I INPUT -p tcp --dport 443 -j ACCEPT
iptables -I INPUT -p tcp -s 10.180.30.200 --dport 80 -j ACCEPT

iptables-save > /etc/iptables.up.rules

cat <<EOF > /etc/network/if-pre-up.d/iptables
#!/bin/bash
/sbin/iptables-restore < /etc/iptables.up.rules
EOF
chmod +x /etc/network/if-pre-up.d/iptables

Hop, épreuve 2 validée, il reste environ 45 minutes… Pour l’épreuve 3, il faut faire un script de backup, qui télécharge des fichiers, vérifie les sommes de contrôle, mail l’admin en cas d’erreur, puis archive le tout en effaçant les archives de plus de 7 jours. Voici ma solution :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#!/bin/bash

URL=http://concours.open-du-web.fr/odw/adminsys-3/OK
MAILS="puyb@puyb.net jury-adminsys@open-du-web.fr"
ARCHIVE=/var/backup/$(date +%F-%T).tar.gz


DIR=$(mktemp -d)

cd "$DIR"
wget --quiet "$URL/files.md5"

for i in $(cut -d' ' -f3 files.md5); do
    wget --quiet "$URL/$i";
done

if ! md5sum -c files.md5 &> md5.log; then
    for i in $MAILS; do
        echo $(echo "Il y a une erreur dans le backup"; cat md5.log) | mail -s"Erreur dans le backup" $i;
    done
else
    mkdir -p "$(dirname $ARCHIVE)";
    tar -zcf $ARCHIVE *
    find "$(dirname $ARCHIVE)" -type f -mtime +7 -exec rm {} \;

fi;

cd -
rm -r "$DIR"

Ainsi que le script d’installation, qui a beaucoup plus au jury, malgré un petit bug qui écrase la crontab :

1
2
3
4
5
#!/bin/bash

cp backup.sh /usr/local/bin
chmod +x /usr/local/bin/backup.sh
echo "* 3  * * *   root    /usr/local/bin/backup.sh" > /etc/crontab

Voilà, il reste à peine 10 minutes, je décide d’aider Kévin dans la réalisation de son CSS. Il le validera quelques secondes avant la fin de l’épreuve.

Damien devait faire un blog en PHP, voici son code et un petit mot de lui :

Première épreuve, on nous donne une base de données et il faut faire une page web qui permet de se logger avec un formulaire (email + password) en créant une session.
Après avoir chargé le .sql dans mysql que je venais d’installer et d’avoir enfin démarré apache avec php activé, j’étais prêt à me lancer.
Globalement pour quelque chose de ce genre là, la partie SQL n’est pas très compliquée, il y avait une petite subtilité ici (1) mais rien de bien méchant. Comme à mon habitude je suis parti sur du PDO (2) pour faire les choses proprement et n’avoir aucun souci d’injection SQL. Un peu de temps pour me remettre dans le bain avec PHP et voilà la première épreuve de dev finie proprement avec tous les points bonus qui fonctionnent (notamment sécurité grâce à PDO et bouton de déconnexion).
Il y avait quelques soucis avec le portail des épreuves donc j’ai essayé d’aider Stéphane sur la partie admin sys (surtout soutenu moralement finalement ;)), une demie-heure plus tard je pouvais enfin passer à l’épreuve d’après.
La deuxième épreuve demandait d’afficher les posts contenus dans la base de donnée comme pour un moteur de blogs, avec possibilité de filtrer l’auteur et une affichage de la date de post un peu évoluée (style « Il y a 35 minutes »). La requète à la base de données est très simple. Après quelques minutes à choisir le format de sortie pour les posts (finalement un div avec titre en h2, sous-titre en h3 et texte derrière) et à fixer l’encoding en utf-8, le plus compliqué a été le formattage de la date, j’ai donc découvert les classes DateTime et DateInterval qui m’ont finalement permis de tout faire proprement !
Pour le filtrage j’ai affichée une petite liste avec les auteurs pour que ça soit plus simple, et puis voilà tout fonctionne bien.
Je m’aperçoit 10 minutes avant la fin en vérifiant le sujet qu’il faut afficher uniquement les 10 derniers posts par ordre chronologique ! Logique pour un blog. Heureusement c’est uniquement une modification à faire dans la requète SQL donc c’est fait sans problème lors des quelques dernières minutes.
Au final c’était assez étonnant de coder dans le froid, les doigts se déplacent moins rapidement ! Et le système d’épreuves m’a un peu rappelé prologin quand j’ai participé il y a plusieurs années ! Un peu de nostalgie, un peu de développement fun, un peu de pression à cause de la limite de temps tout en essayant de faire les choses proprement ! Merci Stéphane de m’avoir invité 🙂
(Petites notes en ce qui concerne le code, en faisant l’épreuve 2 je me suis aperçu que j’avais testé deux fois « user » dans REQUEST au lieu de tester « user » et « pass », et après avoir rendu l’épreuve 2 je me suis dit que j’aurais pu réutiliser la connexion à la base de données au lieu d’en créer une à chaque fois, mais j’ai pas vraiment eu le temps de le faire)

(1) La base de données contenait md5(userid . password). D’habitude on va calculer le hash en php, récupérer le hash depuis SQL et comparer les deux. Mais ici le hash est différent en fonction du userid (et non de l’email). Il y avait donc à mon avis plusieurs façons de faire, la première était de récupérer le userid et le hash, de calculer le hash avec le pot de passe donné et de comparer avec ce qui vient de la base de données, mais j’ai préféré le faire directement dans la base de données puisqu’elle sait le faire.
(2) Le point qui me tient particulièrement à coeur quand je manipule des bases de données, c’est d’éviter les injections SQL, pour ça il existe les « prepared statements » qui sont disponibles notamment dans mysqli, mais je préfère aller encore au-delà en utilisant directement PDO qui est plus intuitive à utiliser et permet même de facilement changer de type de base (mysql/postgresql/sqlite).

Épreuves 1 et 2 du défit « programmation » (zip)

Après avoir discuté un peu avec les recruteurs qui ne se sont pas déjà sauvés en courant ou ne sont pas morts de froid, et avec les autres participants, le verdict tombe : nous somme premier de la catégorie “équipes”… Surprise !

Vu les problèmes de machines virtuelles, le jury a décidé de ne pas nommer tout de suite de gagnant en catégorie admin. La délibération est en cours.

J’ai beaucoup apprécié de jouer ce petit jeu malgré le froid. À refaire !

Maintenant, je pense que l’organisation à encore plein de points a améliorer :
– Trouver un lieu plus accueillant… Je sais avec le concert à la fin, ce n’est pas facile de faire ça dans une salle de cours…
– Ne pas utiliser de Wi-Fi ! Ça lag. Ça se déconnecte. Quand on fait l’épreuve d’admin, avoir un délai de plus d’une seconde entre l’appui sur une touche et le retour à l’écran, c’est affreux…
– Bien tester le moteur de jeu… Les bugs avec les requêtes SQL qui apparaissent au milieu du navigateur, c’est rigolo. On s’est demandé à un moment si déboguer le moteur du jeu n’était pas une épreuve 😉
– Bien vérifier la faisabilité des exercices (les machines virtuelles mal configurées, c’est pas cool)
– Créer des machines virtuelle en avance… Ça évitera de perdre un quart d’heure parce que toutes les machines sont démarrées en même temps
– Préciser dans les exercices ce qui est attendu : pour le premier exercice, je suis partis du principe qu’il fallait configurer directement la machine virtuelle, mais pour les suivants, il a fallu deviner qu’il fallait donner un script et / ou documenter son approche…

Bon, je pinaille peut-être un peu… Mais c’est juste pour que ce soit encore mieux l’année prochaine…

Déc 07 2012

Migration des URL dotclear vers wordpress

Je viens de faire une petite modification du .htaccess du site pour que les anciennes URL du site (qui étaient gérées par dotclear 1.2) ne retournent plus de 404 et pointent vers les bons articles. J’ai aussi ajouté une redirection pour le flux RSS…

1
2
3
RewriteRule ^(\d{4}/\d{2})/\d{2}/\d+-(.*)$ $1/$2/ [P]
RewriteRule ^rss.php /feed/ [P]
RewriteRule ^atom.php /feed/atom/ [P]

Articles plus anciens «