Création de I Skate Paris

Avec les amis du PUC Roller, nous avons décidé de quitter ce dernier pour créer un nouveau club : I Skate Paris

I Skate ParisVous pouvez consulter notre projet sur ce lien

Grace à ce nouveau club, nous allons pouvoir enfin nous lancer librement dans des projets roller intéressants et continuer les 6h de Paris.

Home server (re)installation

Yesterday, after a power outage, the SSD inside my home server fried… As usual, I realised how many things were not backed up after the fact… That’s ironic when you know that one of the purpose of this machine was backuping my stuff !

The roles of the servers are :

  • Internet Gateway
  • Wireless Access Point
  • Storage server
  • Media player
  • Web server (for websites without critical importance, like tests sites, etc)
  • Torrents

In this article (and the next ones), I’ll describe the setup of the new server.

Hardware :

The server is a PC stuffed with many hard drives, 2 ethernet card and a wifi card, hooked up to a TV.

The system is a standard Arch Linux on a SSD. The data hard drives are configured as a BTRFS volume in RAID1.

One NIC is plugged into my ISP fibre adapter, the other one is plugged into my local network.

Network configuration

I used systemd-networkd to configure my network,  hostapd for the wireless access point and dnsmasq for the dhcp/dns.

First, renaming the interfaces :

/etc/systemd/network/10-lan.link

/etc/systemd/network/10-wan.link

/etc/systemd/network/10-wlan0.link

Then I’ve created a new bridge interface. This interface will connect the wired and wireless lan into one network.

/etc/systemd/network/20-br0.netdev

I’ve added the wired lan network to the bridge. The wireless lan network will be added by hostapd.

/etc/systemd/network/20-lan.network

I ‘ve configured the lan network to a staic IP

/etc/systemd/network/30-br0.network

Then I’ve setup the wan network as a DHCP. My internet provider (SFR) require a vendor class starting by neufbox*. I also added IPForward=yes (this used to be done through sysctl or /proc). This command just activate the routing functionality inside the linux kernel. It could be added to any interface.

/etc/systemd/network/30-wan.network

Then I’ve implement my firewall rules :

/etc/iptables/iptables.rules

Wireless access point:

/etc/hostapd/hostapd.conf

Then the DHCP server / DNS cache:

/etc/dnsmasq.conf

Then I’ve enabled all service:

That’s it for the network part. stay tuned for the rest of the config !

 

Utiliser la fibre orange en DHCP

Ce matin, mon router ne pouvait plus se connecter à Internet. Il se trouve que je suis connecté à la fibre par Orange. Je n’utilise pas la livebox.
Le réseau Orange est assez particulier et semble être un assemblage de technologies héritées du passé. On retrouve entre autre des VLAN avec des numéros qui rappellent les circuit VPI/VCI du réseau ATM utilisé en ADSL et du PPPoE, lui aussi hérité de l’ADSL (qui lui l’a hérité des connections RTC)…  Bref, à se demander si derrière le réseau fibre d’orange, il n’y a pas encore un tas de serveur minitel ;-).

Bref, Orange essaye de se moderniser (passage à l’IPv6, je présume), et passe au DHCP. Il ne se débarrasse pas des VLAN, et ne fait pas ça de façon a ce que ce soit simple, mais bon, c’est déjà un mieux. Et c’est pourquoi mon accès internet a cessé de fonctionner correctement ce matin.

En gros, pour se connecter, il faut maintenant passer par le VLAN 832, puis obtenir une IP grâce au DHCP, mais en envoyant des paramètres supplémentaires.
J’ai trouvé ces informations sur le forum de lafibre.info il y a quelques temps. Dans mon réseau, la fibre est branchée via l’adaptateur PON sur un switch capable de gérer les VLAN. Je l’ai donc configuré pour detaguer le VLAN 832 sur le port eth1 de mon routeur, puis j’ai configuré dhclient comme indiqué sur le forum. J’ai aussi changer l’interface utilisée pour la NAT dans iptables de ppp0 à eth1.

/etc/dhclient.conf

Les xx sont à remplacer par le compte client fti/xxxxxx encodé en héxa.

Puis le NAT (/etc/iptables/iptables.rules)

Et pour activer dhclient sur eth1 :

Et hop, c’est réglé

Ma prochaine étape va être de revoir ma config qui est basée sur des scripts fait main pour essayer de migrer sur networkd. Peut être aussi essayer d’en profiter pour agréger mes 2 ports ethernet et faire le detaging du VLAN sous linux plutôt que dans le switch.

Il faudrait aussi que je fasse des articles pour décrire ma config complète (ça peut être utile si je dois la refaire un jour).

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/.

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.

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 !

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 !

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 😉

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

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…