Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
NeoCat committed May 1, 2018
0 parents commit 6143785
Show file tree
Hide file tree
Showing 11 changed files with 475 additions and 0 deletions.
21 changes: 21 additions & 0 deletions LICENSE.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2018 NeoCat

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
125 changes: 125 additions & 0 deletions P3RGB64x32MatrixPanel.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
#include "P3RGB64x32MatrixPanel.h"

volatile SemaphoreHandle_t P3RGB64x32MatrixPanel::timerSemaphore;
P3RGB64x32MatrixPanel* P3RGB64x32MatrixPanel ::singleton;

void IRAM_ATTR P3RGB64x32MatrixPanel::onTimer() {
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;
portENTER_CRITICAL_ISR(&timerMux);

singleton->draw();

portEXIT_CRITICAL_ISR(&timerMux);
xSemaphoreGiveFromISR(timerSemaphore, NULL);
}

void P3RGB64x32MatrixPanel::begin() {
singleton = this;

pinMode(pinR1, OUTPUT);
pinMode(pinG1, OUTPUT);
pinMode(pinB1, OUTPUT);
pinMode(pinR2, OUTPUT);
pinMode(pinG2, OUTPUT);
pinMode(pinB2, OUTPUT);

pinMode(pinLAT, OUTPUT);
pinMode(pinCLK, OUTPUT);
pinMode(pinOE, OUTPUT);

pinMode(pinA, OUTPUT);
pinMode(pinB, OUTPUT);
pinMode(pinC, OUTPUT);
pinMode(pinD, OUTPUT);

digitalWrite(pinLAT, LOW);
digitalWrite(pinCLK, LOW);
digitalWrite(pinOE, HIGH);

timerSemaphore = xSemaphoreCreateBinary();
hw_timer_t* timer = timerBegin(0, 80, true);
timerAttachInterrupt(timer, &onTimer, true);
timerAlarmWrite(timer, 30, true);
timerAlarmEnable(timer);
}

uint16_t P3RGB64x32MatrixPanel::colorHSV(long hue, uint8_t sat, uint8_t val) {
uint8_t r, g, b, lo;
uint16_t s1, v1;

// Hue ( 0 - 1535 )
hue %= 1536;
if (hue < 0) hue += 1536;
lo = hue & 255; // Low byte = primary/secondary color mix
switch (hue >> 8) { // High byte = sextant of colorwheel
case 0 : r = 255 ; g = lo ; b = 0 ; break; // R to Y
case 1 : r = 255 - lo; g = 255 ; b = 0 ; break; // Y to G
case 2 : r = 0 ; g = 255 ; b = lo ; break; // G to C
case 3 : r = 0 ; g = 255 - lo; b = 255 ; break; // C to B
case 4 : r = lo ; g = 0 ; b = 255 ; break; // B to M
default: r = 255 ; g = 0 ; b = 255 - lo; break; // M to R
}

s1 = sat + 1;
r = 255 - (((255 - r) * s1) >> 8);
g = 255 - (((255 - g) * s1) >> 8);
b = 255 - (((255 - b) * s1) >> 8);

v1 = val + 1;
r = (r * v1) >> 11;
g = (g * v1) >> 11;
b = (b * v1) >> 11;

return color555(r, g, b);
}

void P3RGB64x32MatrixPanel::drawPixel(int16_t x, int16_t y, uint16_t color) {
int16_t idx = x + y * 64;
if (idx >= 32 * 64) return;
matrixbuff[idx] = color;
}

void P3RGB64x32MatrixPanel::draw() {
static byte cnt = 31;
static byte y = 15;
static uint32_t out = 0;
y = (y + 1) % 16;

if (y == 0)
cnt = (cnt + 1) % 32;

byte cmp = (cnt >> 4) | ((cnt >> 2) & 0x2) | (cnt & 0x4) | ((cnt << 2) & 0x8) | ((cnt << 4) & 0x10);

for (int x = 0; x < 64; x++) {
bool r1, b1, g1, r2, g2, b2;
uint16_t c = matrixbuff[x + y * 64];
r1 = (c & 0x1f) > cmp;
g1 = ((c >> 5) & 0x1f) > cmp;
b1 = ((c >> 10) & 0x1f) > cmp;
c = matrixbuff[x + (y + 16) * 64];
r2 = (c & 0x1f) > cmp;
g2 = ((c >> 5) & 0x1f) > cmp;
b2 = ((c >> 10) & 0x1f) > cmp;

REG_WRITE(GPIO_OUT_REG, out |
((uint32_t)r1 << pinR1) |
((uint32_t)g1 << pinG1) |
((uint32_t)b1 << pinB1) |
((uint32_t)r2 << pinR2) |
((uint32_t)g2 << pinG2) |
((uint32_t)b2 << pinB2));

REG_WRITE(GPIO_OUT_W1TS_REG, (1 << pinCLK));
}
//REG_WRITE(GPIO_OUT_W1TC_REG, (1 << pinCLK));

REG_WRITE(GPIO_OUT1_W1TS_REG, (1 << pinOE - 32) | (1 << pinLAT - 32));

out = ((y & 1) << pinA) | ((y & 2) << (pinB - 1)) |
((y & 4) << (pinC - 2)) | ((y & 8) << (pinD - 3));
REG_WRITE(GPIO_OUT_REG, out);

for (int x = 0; x < 8; x++) NOP(); // to wait latch and row switch

REG_WRITE(GPIO_OUT1_W1TC_REG, (1 << pinOE - 32) | (1 << pinLAT - 32));
}
38 changes: 38 additions & 0 deletions P3RGB64x32MatrixPanel.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#include "Adafruit_GFX.h"

class P3RGB64x32MatrixPanel : public Adafruit_GFX {
public:
P3RGB64x32MatrixPanel() : Adafruit_GFX(64, 32), matrixbuff({}) {}
void begin(void);
virtual void drawPixel(int16_t x, int16_t y, uint16_t color);

uint16_t color444(uint8_t r, uint8_t g, uint8_t b) { return ((r & 0xf) << 1) | ((uint16_t)(g & 0xf) << 6) | ((uint16_t)(b & 0xf) << 11); }
uint16_t color555(uint8_t r, uint8_t g, uint8_t b) { return (r&0x1f) | ((uint16_t)(g & 0x1f) << 5) | ((uint16_t)(b & 0x1f) << 10); }
uint16_t colorHSV(long hue, uint8_t sat, uint8_t val);

uint16_t matrixbuff[64*32];

private:

static void IRAM_ATTR onTimer(void);
void draw();

const int pinR1 = 25;
const int pinG1 = 26;
const int pinB1 = 27;
const int pinR2 = 21;
const int pinG2 = 22;
const int pinB2 = 23;

const int pinCLK = 15;
const int pinLAT = 32;
const int pinOE = 33;

const int pinA = 12;
const int pinB = 16;
const int pinC = 17;
const int pinD = 18;

static volatile SemaphoreHandle_t timerSemaphore;
static P3RGB64x32MatrixPanel *singleton;
};
52 changes: 52 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# P3RGB64x32MatrixPanel library for ESP32

ESP32 Arduino library for P3 64x32 RGB LED Matrix Panel (available at https://amzn.to/2jlqX1q ).

This software is released under the MIT License, see LICENSE.txt.

# Installation

To download, click the "Download ZIP" button, and specify the zip file
from the "Sketch > Include Library > Add .ZIP Library" menu in the Arduino IDE.

You also need to install "Adafruit_GFX_Library" from the "Library > Manage Libraries" menu.

## Patching GPIO to avoid eratta of ESP32

You should patch the `tools/sdk/ld/esp32.peripherals.ld` to avoid random pixel noise on the display as following:

```
/* PROVIDE ( GPIO = 0x3ff44000 ); */
PROVIDE ( GPIO = 0x60004000 );
```

Please refer section 3.3. in https://www.espressif.com/sites/default/files/documentation/eco_and_workarounds_for_bugs_in_esp32_en.pdf for more details.

# Connection to ESP32 and the LED Matrix Panel

The panel has HUB75 compatible pins.

```
+---------+ Panel - ESP32 pins
| R1 G1 | R1 - IO25 G1 - IO26
| B1 GND | B1 - IO27
| R2 G2 | R2 - IO21 R2 - IO22
| B2 GND | B2 - IO23
| A B | A - IO12 B - IO16
| C D | C - IO17 D - IO18
| CLK LAT | CLK - IO15 LAT - IO32
| OE GND | OE - IO33
+---------+
```

The panel must be powered by 5V AC adapter with enough current capacity.
(Current varies due to how many LED are turned on at the same time.
To drive all the LED, you need 5V4A adapter.)

# Usage

Please check the examples directory for sample programs.

Note that this library heavily use the timer interrupt to drive LED matrix to enable software based 5bit PWM for each line.
This may conflict with WiFi setup, so you should start WiFi before `matrix.begin()` which sets up timer interrupts.

58 changes: 58 additions & 0 deletions examples/ImageFromWiFi/ImageFromWiFi.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#include <WiFi.h>

#include <Adafruit_GFX.h>
#include <Fonts/Picopixel.h>
#include <P3RGB64x32MatrixPanel.h>

P3RGB64x32MatrixPanel matrix;

char ssid[] = "****SSID****";
char pass[] = "**PASSWORD**";
WiFiServer server(20032);

void setup() {
Serial.begin(115200);

Serial.print("Attempting to connect to Network named: ");
Serial.println(ssid); // print the network name (SSID);

WiFi.begin(ssid, pass);
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(1000);
}
Serial.println("");
IPAddress ip = WiFi.localIP();
Serial.print("IP Address: ");
Serial.println(ip);

server.begin(); // start the server

matrix.begin(); // setup the LED matrix

matrix.setTextColor(matrix.color444(0, 0, 8)); // print IP address
matrix.println("IP address");
matrix.setTextColor(matrix.color444(12, 12, 12));
matrix.setFont(&Picopixel);
matrix.println(ip);
}

void loop()
{
WiFiClient client = server.available(); // listen for incoming clients

if (client) {
Serial.println("new client");
unsigned int idx = 0;
while (client.connected()) {
if (client.available()) {
int len = client.read((byte*)matrix.matrixbuff + idx , 64*32*2 - idx);
idx = (idx + len) & (64 * 32 * 2 - 1);
}
}
// close the connection:
client.stop();
Serial.println("client disonnected");
}
}

2 changes: 2 additions & 0 deletions examples/ImageFromWiFi/image_sender/Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
source 'https://rubygems.org'
gem 'mini_magick'
13 changes: 13 additions & 0 deletions examples/ImageFromWiFi/image_sender/Gemfile.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
GEM
remote: https://rubygems.org/
specs:
mini_magick (4.8.0)

PLATFORMS
ruby

DEPENDENCIES
mini_magick

BUNDLED WITH
1.16.0
18 changes: 18 additions & 0 deletions examples/ImageFromWiFi/image_sender/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Setup

```
bundle install
```

Start up ESP32 with "ImageFromWiFi"

# Usage

```
bundle exec ./send.rb 192.168.x.x image.png
bundle exec ./send.rb 192.168.x.x image.jpg
```

The image height must be larger than its width * 2.

The image will be resized to 64 pixels width and scrolled vertically.
33 changes: 33 additions & 0 deletions examples/ImageFromWiFi/image_sender/send.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#!/usr/bin/env ruby
require 'mini_magick'
require 'socket'

if ARGV.size != 2
STDERR.puts "usage: #{$PROGRAM_NAME} ip_address_of_ESP32 image_file_path"
exit 1
end

image = MiniMagick::Image.open ARGV[1]
new_height = image.height * 64 / image.width
image.resize "64x#{new_height}!"
puts "#{image.width}x#{image.height}"
image.contrast
image.gamma 0.3
pixels = image.get_pixels

sock = TCPSocket.open(ARGV[0], 20032)

(new_height - 31).times do |y0|
pix = []
32.times do |y|
64.times do |x|
r, g, b = pixels[y + y0][x].map { |v| v >> 3 }
c = r | (g << 5) | (b << 10)
pix << c
end
end
data = pix.pack('v*')
sock.write data
sleep 0.1
sleep 0.5 if y0 == 0
end
Loading

0 comments on commit 6143785

Please sign in to comment.