Skip to content

Instantly share code, notes, and snippets.

@bczhc
Created June 24, 2024 20:59
Show Gist options
  • Save bczhc/0e02f9ebf96a207f9d1274f7fc567895 to your computer and use it in GitHub Desktop.
Save bczhc/0e02f9ebf96a207f9d1274f7fc567895 to your computer and use it in GitHub Desktop.
宿舍墨水屏 用到的一些代码
#!/bin/env ruby
require 'json'
require 'cgi'
require 'shellwords'
exports = JSON.parse(File.read('/home/bczhc/exports.json'))
response = `curl https://api.coincap.io/v2/rates/bitcoin -s`
btc_price = JSON.parse(response)['data']['rateUsd'].to_f
params = {
electricity: exports['electricity'],
toZeroDate: Time.at(exports['toZeroAt']).strftime('%d %b %H:%M:%S'),
btcPrice: btc_price,
updateAt: Time.now.strftime('%d %b %H:%M:%S'),
}
puts params
params_url_encoded = CGI.escape params.to_json
image_url= "http://192.168.4.1:8081/?name=eink-screen&params=#{params_url_encoded}"
puts image_url
`curl #{Shellwords.escape(image_url)} -s > eink.png`
use std::io::{Read, Write};
use std::net::{SocketAddr, TcpStream};
use std::path::PathBuf;
use clap::Parser;
use image::GenericImageView;
use rand::rngs::OsRng;
use rand::RngCore;
/// ## Coordinate
/// [Width]
/// -----------
/// | |
/// | |
/// | | [Height]
/// | |
/// | |
/// | |
/// -----------
/// ||||
/// Pins
///
/// The top-left point is (0, 0), and the bottom-right one is (width, height).
///
struct ScreenBuffer {
width: usize,
height: usize,
buffer: Vec<u8>,
}
impl ScreenBuffer {
fn new(width: usize, height: usize) -> Self {
if (width * height) % 8 != 0 {
panic!("(width * height) must be a multiple of 8");
}
Self {
width,
height,
buffer: vec![0xff; width * height / 8],
}
}
fn pixel_bit(black: bool) -> bool {
!black
}
fn set_pixel(&mut self, x: usize, y: usize, black: bool) {
assert!(x < EPD_2IN13D_WIDTH && y < EPD_2IN13D_HEIGHT);
let bits_offset = self.width * y + x;
let (byte_n, byte_bit_n) = (bits_offset / 8, (bits_offset % 8) as u32);
self.buffer[byte_n] = n_bit_set(self.buffer[byte_n], byte_bit_n, Self::pixel_bit(black));
}
fn set_pixel_n(&mut self, pixel_n: usize, black: bool) {
assert!(pixel_n < self.width * self.height);
let (byte_n, byte_bit_n) = (pixel_n / 8, (pixel_n % 8) as u32);
self.buffer[byte_n] = n_bit_set(self.buffer[byte_n], byte_bit_n, Self::pixel_bit(black));
}
fn get_pixel(&mut self, x: usize, y: usize) -> bool {
assert!(x < EPD_2IN13D_WIDTH && y < EPD_2IN13D_HEIGHT);
let bits_offset = self.width * y + x;
let (byte_n, byte_bit_n) = (bits_offset / 8, (bits_offset % 8) as u32);
n_bit(self.buffer[byte_n], byte_bit_n)
}
}
#[derive(Debug, clap::Parser)]
struct Args {
image: PathBuf,
}
fn main() -> anyhow::Result<()> {
let args = Args::parse();
let mut buffer = ScreenBuffer::new(EPD_2IN13D_WIDTH, EPD_2IN13D_HEIGHT);
let image_path = args.image;
let image = image::open(image_path)?;
let (image_w, image_h) = image.dimensions();
if image_w as usize != EPD_2IN13D_HEIGHT || image_h as usize != EPD_2IN13D_WIDTH {
panic!(
"Wrong image specification: width and height must be {} and {}",
EPD_2IN13D_HEIGHT, EPD_2IN13D_WIDTH
);
}
let mut pixel_n = 0;
for x in 0..image_w {
for y in (0..image_h).rev() {
let pixel = image.get_pixel(x, y);
let black = pixel.0[..3] == [0x00, 0x00, 0x00];
buffer.set_pixel_n(pixel_n, black);
pixel_n += 1;
}
}
update_screen(&buffer.buffer)?;
Ok(())
}
/// `n` is counted from the MSB (left-to-right), and starts from zero.
fn n_bit_set(mut b: u8, n: u32, is_one: bool) -> u8 {
((b.rotate_left(n + 1) & 0b11111110) | u8::from(is_one)).rotate_right(n + 1)
}
/// `n` is counted from the MSB (left-to-right), and starts from zero.
fn n_bit(b: u8, n: u32) -> bool {
b.rotate_left(n + 1) % 2 == 1
}
fn update_screen(image: &[u8]) -> anyhow::Result<()> {
let mut stream = TcpStream::connect("192.168.4.12:5002".parse::<SocketAddr>().unwrap())?;
stream.write_all(&build_directive_string("update"))?;
stream.write_all(image)?;
let mut response = String::new();
stream.read_to_string(&mut response)?;
println!("{}", response);
Ok(())
}
fn build_directive_string(d: &str) -> [u8; 16] {
let mut buf = [0_u8; 16];
buf[..d.len()].copy_from_slice(d.as_bytes());
buf
}
pub const EPD_2IN13D_WIDTH: usize = 104;
pub const EPD_2IN13D_HEIGHT: usize = 212;
fn checkerboard_image_buffer() -> Vec<u8> {
let mut image_buffer = vec![0_u8; EPD_2IN13D_WIDTH / 8 * EPD_2IN13D_HEIGHT];
let row_bytes = EPD_2IN13D_WIDTH / 8;
for i in 0..EPD_2IN13D_HEIGHT {
let byte_filled = match i % 2 {
0 => 0b01010101_u8,
1 => 0b10101010_u8,
_ => unreachable!(),
};
image_buffer[(row_bytes * i)..(row_bytes * i + row_bytes)].fill(byte_filled);
}
image_buffer
}
fn new_white_image_buffer() -> Vec<u8> {
let mut buf = vec![0_u8; EPD_2IN13D_WIDTH / 8 * EPD_2IN13D_HEIGHT];
buf.fill(0xff);
buf
}
fn random_pattern() -> Vec<u8> {
let mut image_buffer = vec![0_u8; EPD_2IN13D_WIDTH / 8 * EPD_2IN13D_HEIGHT];
OsRng.fill_bytes(&mut image_buffer);
image_buffer
}
#[cfg(test)]
mod test {
use crate::{n_bit, n_bit_set};
#[test]
#[allow(clippy::bool_assert_comparison)]
fn bits() {
assert_eq!(n_bit(0b10101010, 0), true);
assert_eq!(n_bit(0b10101010, 1), false);
assert_eq!(n_bit(0b10101010, 2), true);
assert_eq!(n_bit(0b10101010, 3), false);
assert_eq!(n_bit(0b10101010, 4), true);
assert_eq!(n_bit(0b10101010, 5), false);
assert_eq!(n_bit(0b10101010, 6), true);
assert_eq!(n_bit(0b10101010, 7), false);
assert_eq!(n_bit_set(0b10000110, 3, true), 0b10010110);
assert_eq!(n_bit_set(0b10000110, 0, false), 0b00000110);
assert_eq!(n_bit_set(0b10101101, 3, true), 0b10111101);
assert_eq!(n_bit_set(0b01101001, 6, false), 0b01101001);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment