Rickrolling with a Pharmacy cross
Rickrolling with a Pharmacy cross
Rickrolling with a Pharmacy cross 2026-09-28 用药店十字招牌进行“瑞克摇”(Rickroll) 2026年9月28日
A little bit of history
一点背景历史
If you have ever visited France, you might have seen one of these green crosses, bolted onto the building, with bright flashing colors. It is a symbol commonly used to indicate that there is a pharmacy nearby (symbols might vary on a country basis). At this point, it has become a national symbol. 如果你曾去过法国,你可能见过那些安装在建筑物外墙上、闪烁着明亮色彩的绿色十字招牌。这是当地常见的药店标志(不同国家的标志可能有所不同)。如今,它已成为法国的一种国民符号。
The pharmacy crosses can be a true business for some companies, where they make (or resell) crosses and implement their own control software. However, nothing lasts forever, and turns out that’s true for pharmacies too! People retire, quit the business and whatnot. Question is: what happens to these crosses? Most of them can cost anywhere from 200 to 2000€ depending on the brand, so it’s not unusual that these crosses eventually find themselves on the e-commerce market (ebay, leboncoin…). 对一些公司来说,药店十字招牌是一门真正的生意,他们制造(或转售)这些招牌并开发配套的控制软件。然而,没有什么是永恒的,药店也不例外!人们会退休、转行等等。问题是:这些招牌最后去哪了?根据品牌不同,它们的价格从200欧元到2000欧元不等,因此这些招牌最终出现在二手电商平台(如eBay、leboncoin等)上也就不足为奇了。
About two years ago, a strong interest was given to these crosses due to a very well-produced and popular video by French youtuber Sylvqin. In this video, he explains the history behind these crosses, goes to buy one and then reverse engineers it. 大约两年前,法国YouTuber Sylvqin制作了一段非常受欢迎的视频,引发了人们对这些招牌的浓厚兴趣。在视频中,他讲解了这些招牌背后的历史,并亲自购买了一个进行逆向工程。
Pre-tinkering
动手之前
At the start of 2026, a couple of older students at my school decided to challenge themselves too. So they bought one and completely reversed engineered it, which took them weeks. After they were successful, they organized a hackathon where we could play with the cross! Unfortunately, I didn’t participate in the reversing process, so I cannot comment on how the cross works or any of it. If there’s an article about it, let me know and I will link it here! 2026年初,我校的几位学长决定挑战自我。他们买了一个招牌并完成了逆向工程,这花费了他们数周时间。成功后,他们组织了一场黑客马拉松,让我们有机会亲自操作这个招牌!遗憾的是,我没有参与逆向过程,所以无法评价它的工作原理。如果有相关的文章,请告诉我,我会把它链接在这里!
They gave us a C++ library, called Lib_Croix, to directly interact with it. It simplified a lot of reading & writing to the cross. There were a couple of submissions such as: A working Tetris, A real-time editor using a Python server, Game Of Life, And the submission from my group of friends and me, a “video” player.
他们提供了一个名为 Lib_Croix 的C++库,用于直接与招牌交互。它极大地简化了对招牌的读写操作。当时涌现了几个作品,例如:可运行的俄罗斯方块、使用Python服务器的实时编辑器、生命游戏,以及我和朋友们提交的作品——一个“视频”播放器。
As you might know, a video is essentially just a collection of images put together. So first, to display a video, we need to try and just display an image. Warning: The following code was made in less than 24 hours which means it is of mediocre quality. If I had more time (and more energy), I would’ve done it probably way differently. 众所周知,视频本质上就是一系列图像的集合。因此,要播放视频,我们首先得尝试显示一张图像。警告:以下代码是在不到24小时内完成的,质量平平。如果我有更多的时间(和精力),我可能会用完全不同的方式来实现。
Displaying a simple image
显示静态图像
We need some information about our local cross, let’s get those: Only taking the biggest points (middle horizontal axis X, and middle vertical axis Y), the cross is 24 LEDs wide and 24 LEDs high. It only supports two colors (on (green) or off). It has two sides (front and back). 我们需要了解一下手头招牌的参数:取其最大尺寸(水平中轴X和垂直中轴Y),该招牌宽24个LED,高24个LED。它仅支持两种颜色(开启(绿色)或关闭)。它有两面(正面和背面)。
For simplicity sake, we will convert our image from X by Y to 24 by 24 and make it monochrome. Of course, we’re going to use imagemagick for that:
为了简化处理,我们将图像从原始尺寸转换为24x24像素并转为单色。当然,我们要使用 imagemagick 来实现:
convert image.png -resize 24x24 -monochrome image.converted
That’s pretty simple already, but since I don’t really want to bother with C++ image handling, let’s make it even simpler. I created a python script (using Pillow) to take all the pixels on the image and then create a text file with an x for white pixels and . for black pixels:
这已经很简单了,但由于我不想在C++中处理图像,我们可以做得更简单。我写了一个Python脚本(使用Pillow库),遍历图像的所有像素,并生成一个文本文件,用 x 表示白色像素,用 . 表示黑色像素:
im = Image.open(file)
rgb_im = im.convert('RGB')
width, height = rgb_im.size
# Yes, the "format" is called .pharma
with open(file.replace(".converted", ".pharma"), "w+") as f:
for y in range(height):
for x in range(width):
r, g, b = rgb_im.getpixel((x, y))
if r == 0 and g == 0 and b == 0:
f.write(".")
else:
f.write("x")
f.write("\n")
Future self note: you maybe could’ve just used PPM instead of dealing with all of that. 给未来的自己:也许直接用PPM格式会更好,不用折腾这些。
Last but not least, the C++ displayImage function (simplified for the example):
最后,这是C++的 displayImage 函数(为示例进行了简化):
void displayImage() {
// Reset the bitmap, put it all at 0
memset(bitmap, 0, sizeof(bitmap));
for (std::size_t y = 0; y < lines.size(); y++) {
for (std::size_t x = 0; x < lines[y].length(); x++) {
// That's a white pixel, lit it!
if (lines[y][x] == 'x') bitmap[y][x] = 1;
}
}
// Write the bitmap to the cross
cross.writeBitmap(bitmap);
}
Woo! We displayed an image of the cross! (forgot to take a picture, but it was our GitHub organization’s logo, a remix of the sudo sandwich logo). Now, let’s do it for a video. 哇!我们成功在招牌上显示了一张图片!(忘了拍照,但那是我们GitHub组织的Logo,一个sudo三明治Logo的变体)。现在,让我们来播放视频。
Displaying a video
播放视频
Okay so, videos == lots of images, but how can we extract lots of images? Thankfully, a small software that powers a significant part of the Internet can help us with that (FFmpeg). Let’s write a quick bash script for that: 好了,视频等于大量的图像,但我们如何提取这些图像呢?幸运的是,支撑互联网很大一部分功能的软件 FFmpeg 可以帮我们搞定。让我们写一个简单的Bash脚本:
VIDEO="$1"
convert_bw_image() { convert $1 -resize 24x24 -monochrome $1.converted }
convert_all_bw_images() {
FILES=./output/*.png
for f in $FILES; do convert_bw_image $f; done
}
rm -rf output/
mkdir -p output/
ffmpeg -i $VIDEO -vf fps=10 output/out%d.png
convert_all_bw_images
It will create a folder output where all of our .converted monochrome files reside. Our python script, which has been modified to include loops, will then transform those files into .pharma files. Now some file sorting machinery for our C++ files collection:
它会创建一个 output 文件夹,存放所有 .converted 单色文件。我们修改后的Python脚本(加入了循环)会将这些文件转换为 .pharma 文件。现在,我们需要为C++文件集合编写一些排序逻辑:
void getAllPharmaFiles() {
const std::string path = "output/";
for (const auto &entry : std::filesystem::directory_iterator(path)) {
if (hasEnding(entry.path().string(), ".pharma")) {
pharmaFiles.push_back(entry.path().string());
}
}
auto compare = [](const std::string &a, const std::string &b) {
std::regex rgx("[0-9]+");
std::smatch match_a; std::smatch match_b;
std::string a_nb, b_nb;
if (std::regex_search(a.begin(), a.end(), match_a, rgx)) a_nb = match_a[0];
if (std::regex_search(b.begin(), b.end(), match_b, rgx)) b_nb = match_b[0];
return std::stoi(a_nb) < std::stoi(b_nb);
};
std::sort(pharmaFiles.begin(), pharmaFiles.end(), compare);
}
And then voila! The final version was worth it :) You can see if you ever come to Epitech Lyon, you should come by to see the cross as it now lives as a permanent display. 搞定!最终版本非常值得 :) 如果你哪天来到里昂的Epitech学校,一定要来看看这个招牌,它现在已经成为一个永久的展示品了。