Skip to content

sava-74/SavaOLED_ESP32

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

34 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

📖 Полная документация библиотеки SavaOLED_ESP32

Библиотека предназначена для управления OLED дисплеями на базе контроллера SSD1306 через интерфейс I2C на микроконтроллерах ESP32.

v1.1.0 02.01.2026

📑 Оглавление

  1. Подключение и Константы
  2. Конструктор и Инициализация
  3. Настройки текста и режимов
  4. Управление курсором
  5. Печать текста (Накопление)
  6. Отрисовка текста (Рендеринг)
  7. Графические примитивы
  8. Работа с буфером и Экраном
  9. Аппаратное управление дисплеем
  10. Получение информации (Getters)
  11. Доступные шрифты
  12. Отладка (Debug Mode)
  13. Примеры использования

1. Подключение и Константы

Перед началом работы подключите библиотеку и необходимые шрифты.

#include <Arduino.h>
#include "SavaOLED_ESP32.h"
#include "Fonts/SF_Font_P8.h" // Основной шрифт 8px с поддержкой кириллицы CP1251

Полезные константы (для аргументов)

Режимы отрисовки (mode):

  • REPLACE — Заменяет пиксели (рисует 1, стирает 0). По умолчанию.
  • ADD_UP — Сложение (рисует 1, 0 прозрачен).
  • INV_AUTO — Инверсия (XOR).
  • ERASE — Ластик (рисует 0).
  • ERASE_BORDER — Стирает фигуру, затем рисует её контур.

Выравнивание текста (align):

  • StrLeft — По левому краю.
  • StrCenter — По центру.
  • StrRight — По правому краю.
  • StrScroll — Режим бегущей строки.

Заливка (fill):

  • FILL (true) — Залитая фигура.
  • NO_FILL (false) — Только контур.

2. Конструктор и Инициализация

SavaOLED_ESP32 (Конструктор)

Создает экземпляр объекта. Вызывается глобально.

SavaOLED_ESP32(uint8_t width = 128, uint8_t height = 64, i2c_port_t port = I2C_NUM_0);
  • width: Ширина экрана. По умолчанию: 128.
  • height: Высота экрана (64 или 32). По умолчанию: 64.
  • port: Порт I2C ESP32 (I2C_NUM_0 или I2C_NUM_1). По умолчанию: I2C_NUM_0.

Примеры:

SavaOLED_ESP32 oled(); // 128x64, порт 0
SavaOLED_ESP32 oled(128, 32); // 128x32, порт 0
SavaOLED_ESP32 oled(128, 64, I2C_NUM_1); // 128x64, порт 1

setAddress

Задает I2C адрес устройства. Вызывать до init().

void setAddress(uint8_t address = 0x3C);
  • address: Адрес устройства. Обычно 0x3C или 0x3D. По умолчанию: 0x3C.

init

Инициализирует шину I2C и дисплей. Обязательно вызывать в setup().

void init(uint32_t freq = 400000, int8_t _sda = 21, int8_t _scl = 22);
  • freq: Частота шины в Гц. По умолчанию: 400000 (400 кГц).
  • _sda: GPIO пин линии данных. По умолчанию: 21.
  • _scl: GPIO пин линии тактирования. По умолчанию: 22.

Примеры:

oled.init(); // Стандартные настройки
oled.init(1000000, 4, 5); // 1 МГц, SDA на 4 пине, SCL на 5

Max 1200000, 1.2мГц


3. Настройки текста и режимов

font

Устанавливает активный шрифт для последующих команд print.

void font(const savaFont &fontPtr);
  • fontPtr: Имя структуры шрифта (из подключенного .h файла).

drawMode

Устанавливает глобальный режим наложения пикселей для текста и примитивов (если в примитиве не указан свой).

void drawMode(uint8_t mode = REPLACE);
  • mode: REPLACE, ADD_UP, INV_AUTO и т.д.

charSpacing

Устанавливает расстояние между символами в пикселях.

void charSpacing(uint8_t spacing);
  • spacing: Число пикселей отступа. По умолчанию (в конструкторе): 1.

scroll

Включает или выключает режим прокрутки для текста, напечатанного с выравниванием StrScroll.

void scroll(bool enabled, bool scrollReset = false);
  • enabled: true — включить, false — выключить.
  • scrollReset false - работает, true обнулить координату X для скроллинга (Надпись будет находится в начальном положении)

scrollSpeed

Настраивает параметры горизонтальной прокрутки.

void scrollSpeed(uint8_t speed = 3, bool loop = true);
  • speed: Скорость от 1 (медленно) до 15 (очень быстро). По умолчанию: 3.
  • loop: true — бесконечная прокрутка, false — прокрутить один раз и остановиться. По умолчанию: true.

scrollSpeedVert

Настраивает скорость вертикальной прокрутки (используется в drawPrintVert).

void scrollSpeedVert(uint8_t speed = 3);
  • speed: Скорость от 1 до 15. По умолчанию: 3.

setBuffer

Устанавливает метод передачи данных на экран.

void setBuffer(bool enabled);
  • enabled:
    • true (FULL) — отправлять буфер целиком за одну транзакцию (быстрее, но требует большого буфера I2C).
    • false (PAGES) — отправлять постранично (надежнее, стандартно). По умолчанию: false.

4. Управление курсором

Перед использованием print() необходимо установить курсор.

cursor

Задает позицию и область для вывода текста.

void cursor(int16_t x, int16_t y, uint8_t align = StrLeft, int16_t x2 = -1);
  • x: Координата X (левый край или точка привязки).
  • y: Координата Y (верхний край строки).
  • align: Режим выравнивания (StrLeft, StrCenter, StrRight, StrScroll). По умолчанию: StrLeft.
  • x2: Правая граница области печати. Если -1, то используется ширина экрана. По умолчанию: -1.

Примеры:

oled.cursor(0, 0); // Левый верхний угол
oled.cursor(0, 10, StrCenter); // Центрировать по ширине экрана
oled.cursor(10, 10, StrLeft, 60); // Писать в области от X=10 до X=60

5. Печать текста (Накопление)

Эти функции не рисуют текст сразу, а добавляют его во внутренний буфер строки. Поддерживается цепочка вызовов.

print (Строка)

void print(const char* text);
void print(const String &s);
  • text / s: Текст для вывода. Поддерживает кириллицу (CP1251 перекодируется автоматически).

print (Целые числа)

void print(int value, uint8_t min_digits = 0);
// Также перегрузки для: int8_t, uint8_t, int16_t, uint16_t, int32_t, uint32_t
  • value: Число для вывода.
  • min_digits: Минимальное количество цифр. Если число короче, оно дополняется нулями слева. По умолчанию: 0 (без нулей).

Пример: oled.print(5, 3) выведет "005".

print (Дробные числа)

void print(float value, uint8_t decimalPlaces = 2, uint8_t min_width = 0);
void print(double value, uint8_t decimalPlaces = 2, uint8_t min_width = 0);
  • value: Число.
  • decimalPlaces: Количество знаков после запятой. По умолчанию: 2.
  • min_width: Минимальная общая ширина строки (дополняется пробелами слева). По умолчанию: 0.

6. Отрисовка текста (Рендеринг)

Эти функции берут всё, что было накоплено через print(), и рисуют в видеопамять.

drawPrint

Стандартная отрисовка горизонтальной строки текста.

void drawPrint();

Отрисовывает текст согласно настройкам cursor(), font(), drawMode().

drawPrintVert

Отрисовка вертикального текста (сверху вниз).

void drawPrintVert();

Примечание: Шрифт должен быть повернут на 90 градусов в редакторе шрифтов, либо эта функция сама укладывает буквы столбиком (зависит от реализации шрифта). Использует scrollSpeedVert для прокрутки.


6.1. Работа со скроллингом текста

Библиотека поддерживает два типа скроллинга: горизонтальный (бегущая строка слева направо) и вертикальный (текст сверху вниз).

Горизонтальный скроллинг

Когда использовать:

  • Текст не помещается в отведенную область
  • Нужна бегущая строка (ticker)
  • Длинные названия в меню

Как настроить:

  1. Установите курсор с режимом StrScroll
  2. Включите скроллинг через scroll(true)
  3. Настройте скорость через scrollSpeed()
  4. Вызовите drawPrint()

Пример:

// Бегущая строка на всю ширину экрана
oled.cursor(0, 0, StrScroll);           // Режим скроллинга
oled.font(SF_Font_P8);
oled.scrollSpeed(5, true);              // Скорость 5, зацикленный режим
oled.scroll(true);                      // Включить скроллинг
oled.print("Длинный текст который не помещается на экране");
oled.drawPrint();

Пример с ограниченной областью:

// Скроллинг только в области от X=20 до X=120
oled.cursor(20, 10, StrScroll, 120);    // Область [20..120]
oled.scrollSpeed(3, true);
oled.scroll(true);
oled.print("Пункт меню с длинным названием");
oled.drawPrint();

Важно:

  • Скроллинг работает только с режимом StrScroll
  • Если текст помещается в область, скроллинг не активируется
  • Используйте loop = true для бесконечной прокрутки
  • Используйте loop = false для однократной прокрутки и остановки

Вертикальный скроллинг

Когда использовать:

  • Вертикальные надписи сбоку экрана
  • Бегущая строка сверху вниз
  • Дизайнерские элементы интерфейса

Как настроить:

  1. Используйте вертикальный шрифт SF_Vertical_P8
  2. Установите курсор с режимом StrScroll
  3. Настройте скорость через scrollSpeedVert()
  4. Вызовите drawPrintVert() (не drawPrint!)

Пример:

oled.cursor(0, 0, StrScroll);           // Режим скроллинга
oled.font(SF_Vertical_P8);              // Вертикальный шрифт
oled.scrollSpeedVert(10);               // Скорость вертикального скроллинга
oled.scroll(true);                      // Включить скроллинг
oled.print("Вертикальная бегущая строка");
oled.drawPrintVert();                   // Вертикальная отрисовка

Комбинирование горизонтального и вертикального скроллинга

// Левая сторона: вертикальный скроллинг
oled.cursor(0, 0, StrScroll);
oled.font(SF_Vertical_P8);
oled.scrollSpeedVert(10);
oled.scroll(true);
oled.print("Реклама");
oled.drawPrintVert();

// Правая сторона: горизонтальный скроллинг
oled.cursor(20, 10, StrScroll, 127);
oled.font(SF_Font_P8);
oled.scrollSpeed(5, true);
oled.scroll(true);
oled.print("Главное меню: выбор опций");
oled.drawPrint();

Управление скроллингом

Включение/выключение:

oled.scroll(true);   // Включить
oled.scroll(false);  // Выключить (текст будет обрезан по границам области)

Настройка скорости горизонтального скроллинга:

oled.scrollSpeed(1, true);   // Очень медленно, зацикленный
oled.scrollSpeed(5, true);   // Средняя скорость (рекомендуется)
oled.scrollSpeed(15, true);  // Очень быстро
oled.scrollSpeed(3, false);  // Прокрутить один раз и остановиться

Настройка скорости вертикального скроллинга:

oled.scrollSpeedVert(1);     // Очень медленно
oled.scrollSpeedVert(10);    // Средняя скорость (рекомендуется)
oled.scrollSpeedVert(15);    // Очень быстро

Что можно и что нельзя

✅ МОЖНО:

  • Использовать несколько независимых скроллингов на одном экране
  • Комбинировать горизонтальный и вертикальный скроллинг
  • Ограничивать область скроллинга параметром x2 в cursor()
  • Использовать разные скорости для разных строк
  • Переключать режимы скроллинга динамически через scroll(true/false)

❌ НЕЛЬЗЯ:

  • Использовать скроллинг без режима StrScroll в cursor()
  • Применять вертикальный скроллинг к обычным (горизонтальным) шрифтам
  • Использовать drawPrint() для вертикального шрифта (нужен drawPrintVert())
  • Скроллить графические примитивы (только текст)

Практический пример: меню с автоскроллингом

const char* menuItems[] = {"Настройки", "Очень длинное название пункта меню", "Wi-Fi"};
uint8_t selectedItem = 0;

void loop() {
  oled.clear();

  for (uint8_t i = 0; i < 3; i++) {
    uint8_t y = i * 12;

    if (i == selectedItem) {
      // Выбранный пункт - с автоскроллингом
      oled.cursor(10, y, StrScroll, 120);
      oled.scrollSpeed(5, true);
      oled.scroll(true);
    } else {
      // Обычный пункт - без скроллинга
      oled.cursor(10, y, StrLeft);
      oled.scroll(false);
    }

    oled.font(SF_Font_P8);
    oled.print(menuItems[i]);
    oled.drawPrint();
  }

  oled.display();
}

7. Графические примитивы

Координаты начинаются с 0. Максимум (width-1, height-1).

dot (Точка)

void dot(int16_t x, int16_t y, uint8_t mode = REPLACE);

line (Линия)

Рисует произвольную линию (алгоритм Брезенхэма).

void line(int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint8_t mode = REPLACE);

hLine (Горизонтальная линия)

Быстрая отрисовка горизонтальной линии.

void hLine(int16_t x, int16_t y, int16_t w, uint8_t mode = REPLACE);
  • w: Длина линии.

vLine (Вертикальная линия)

Быстрая отрисовка вертикальной линии.

void vLine(int16_t x, int16_t y, int16_t h, uint8_t mode = REPLACE);
  • h: Высота линии.

rect (Прямоугольник)

void rect(int16_t x, int16_t y, int16_t w, int16_t h, uint8_t mode = REPLACE, bool fill = NO_FILL);
  • fill: true — залить, false — только рамка.

rectR (Скругленный прямоугольник)

void rectR(int16_t x, int16_t y, int16_t w, int16_t h, int16_t r, uint8_t mode = REPLACE, bool fill = NO_FILL);
  • r: Радиус скругления углов.

circle (Круг)

void circle(int16_t x0, int16_t y0, int16_t r, uint8_t mode = REPLACE, bool fill = NO_FILL);

bezier (Кривая Безье)

Рисует плавную кривую по трем точкам (начало, контрольная, конец).

void bezier(int16_t x0, int16_t y0, int16_t x1, int16_t y1, int16_t x2, int16_t y2, uint8_t mode = REPLACE);
  • x1, y1: Контрольная точка (изгиб тянется к ней).

Пример:

// Плавная дуга от левого нижнего к правому верхнему углу
oled.bezier(0, 60, 64, 10, 127, 0, REPLACE);

drawPeak (Пик)

Рисует кривую, которая гарантированно проходит через вершину (пик). Удобно для графиков.

void drawPeak(int16_t x0, int16_t y0, int16_t x_peak, int16_t y_peak, int16_t x2, int16_t y2, uint8_t mode = REPLACE);
  • x_peak, y_peak: Координаты вершины.

Пример:

// График с пиком в центре экрана
oled.drawPeak(0, 32, 64, 10, 127, 32, REPLACE);  // Пик вверх
oled.drawPeak(0, 32, 64, 50, 127, 32, REPLACE);  // Пик вниз

drawBitmap (Картинка)

Рисует монохромное изображение из массива байт.

void drawBitmap(int16_t x, int16_t y, const uint8_t* bitmap, int16_t w, int16_t h, uint8_t mode = REPLACE);
  • bitmap: Указатель на массив данных (формат: вертикальные байты, младший бит сверху).
  • w: Ширина изображения в пикселях.
  • h: Высота изображения в пикселях.

Формат данных битмапа:

  • Данные организованы по вертикальным колонкам (столбцам)
  • Каждый байт содержит 8 вертикальных пикселей
  • Младший бит (0x01) - верхний пиксель, старший бит (0x80) - нижний
  • Если высота не кратна 8, неиспользуемые биты в последнем байте колонки игнорируются

Пример:

// Иконка сердца 8x8 пикселей
const uint8_t heart_8x8[] = {
  0x0C, 0x1E, 0x3F, 0x7E, 0x7E, 0x3F, 0x1E, 0x0C
};

oled.drawBitmap(60, 28, heart_8x8, 8, 8, REPLACE);

fillScreen (Заливка)

Заполняет весь экран паттерном.

void fillScreen(uint8_t pattern);
  • pattern: Байт-маска.
    • 0x00 — Очистить (черный).
    • 0xFF — Залить (белый).
    • 0x55 — Черезстрочная "зебра".

8. Работа с буфером и Экраном

clear

Очищает программный буфер дисплея (заполняет нулями).

void clear();

display

Самая важная команда. Отправляет содержимое буфера на физический экран по I2C.

void display();

9. Аппаратное управление дисплеем

contrast

Регулирует яркость дисплея.

void contrast(uint8_t value);
  • value: 0 (минимум) ... 255 (максимум).

power

Включает или выключает экран (Sleep mode).

void power(bool mode);
  • mode: true — включить, false — выключить.

invertDisplay

Аппаратная инверсия цветов (белое становится черным без перерисовки буфера).

void invertDisplay(bool mode);

flipH / flipV

Зеркальное отражение изображения.

void flipH(bool mode); // Горизонтально
void flipV(bool mode); // Вертикально

rotation

Поворот экрана на 180 градусов.

void rotation(bool rotate180);
  • rotate180: true — перевернутый режим, false — нормальный.

10. Получение информации (Getters)

Функции для получения текущих параметров состояния.

  • int16_t getCursorX() const — Текущая координата X курсора.
  • int16_t getCursorY() const — Текущая координата Y курсора.
  • uint16_t getTextWidth() const — Ширина последней отрисованной строки текста в пикселях.
  • uint16_t getTextHeight() const — Высота текущего шрифта.
  • uint16_t getScopeCursor() const — Ширина текущей области курсора (значение x2 или ширина экрана).
  • bool isReady() const — Проверка готовности дисплея. Возвращает true если дисплей инициализирован и готов к работе, false если есть проблемы с I2C.

11. Доступные шрифты

Библиотека поставляется с набором готовых шрифтов. Все шрифты находятся в папке src/Fonts/.

Стандартные шрифты с кириллицей (CP1251)

SF_Font_P8 — Основной пропорциональный шрифт 8px

#include "Fonts/SF_Font_P8.h"
oled.font(SF_Font_P8);
  • Высота: 8 пикселей
  • Поддержка: ASCII + Кириллица (CP1251)
  • Тип: Пропорциональный (переменная ширина символов)
  • Использование: Основной шрифт для текста

SF_Font_x2_P16 — Увеличенный пропорциональный шрифт 16px

#include "Fonts/SF_Font_x2_P16.h"
oled.font(SF_Font_x2_P16);
  • Высота: 16 пикселей
  • Поддержка: ASCII + Кириллица (CP1251)
  • Тип: Пропорциональный (в 2 раза больше SF_Font_P8)
  • Использование: Крупные заголовки и важный текст

SF_Vertical_P8 — Вертикальный шрифт 8px

#include "Fonts/SF_Vertical_P8.h"
oled.font(SF_Vertical_P8);
oled.drawPrintVert(); // Используется с вертикальной отрисовкой
  • Высота: 8 пикселей (повернут на 90°)
  • Поддержка: ASCII + Кириллица (CP1251)
  • Тип: Пропорциональный, вертикальный
  • Использование: Вертикальные надписи, бегущая строка сверху вниз

Специальные шрифты

SF_7Seg_Temper_NM10x14 — Семисегментный шрифт для температуры

#include "Fonts/SF_7Seg_Temper_NM10x14.h"
oled.font(SF_7Seg_Temper_NM10x14);
  • Размер: 10x14 пикселей
  • Поддержка: Цифры 0-9, точка, минус
  • Тип: Моноширинный (фиксированная ширина)
  • Специальные символы:
    • ":" → Символ градуса (t°)
    • "+" → Буква C (Цельсий)
    • "/" → Капли
    • "*" → Символ влажности (H)
    • "=" → Знак равенства
  • Использование: Отображение температуры, цифровые индикаторы

SF_Icon_NP8 — Набор иконок 8px

#include "Fonts/SF_Icon_NP8.h"
oled.font(SF_Icon_NP8);
  • Размер: ~8 пикселей (разные иконки имеют разную ширину)
  • Поддержка: Специальные символы и иконки
  • Тип: Непропорциональный
  • Использование: Графические элементы интерфейса, индикаторы состояния

SF_Mini_NP5 — Миниатюрный шрифт 5px

#include "Fonts/SF_Mini_NP5.h"
oled.font(SF_Mini_NP5);
  • Высота: 5 пикселей
  • Поддержка: Цифры 0-9, точка, базовые символы
  • Тип: Непропорциональный
  • Использование: Компактный вывод цифр, мелкий текст

Пример комбинирования шрифтов

// Заголовок крупным шрифтом
oled.cursor(64, 0, StrCenter);
oled.font(SF_Font_x2_P16);
oled.print("Температура");
oled.drawPrint();

// Значение семисегментным шрифтом
oled.cursor(64, 20, StrCenter);
oled.font(SF_7Seg_Temper_NM10x14);
oled.print("+");              // Буква C
oled.print(23.5, 1);          // Температура
oled.print(":");              // Символ °
oled.drawPrint();

// Описание стандартным шрифтом
oled.cursor(64, 40, StrCenter);
oled.font(SF_Font_P8);
oled.print("в комнате");
oled.drawPrint();

12. Отладка (Debug Mode)

Библиотека поддерживает режим отладки для вывода диагностической информации в Serial Monitor.

Включение режима отладки

Добавьте #define SAVAOLED_DEBUG ДО подключения библиотеки в вашем скетче:

#define SAVAOLED_DEBUG  // Включить отладочные сообщения

#include <Arduino.h>
#include "SavaOLED_ESP32.h"
#include "Fonts/SF_Font_P8.h"

void setup() {
    Serial.begin(115200);  // Обязательно инициализируйте Serial!
    oled.init();
    // Теперь вы увидите отладочные сообщения в Serial Monitor
}

Типы отладочных сообщений

В режиме отладки библиотека выводит:

  • [SavaOLED] — Информационные сообщения (инициализация, статус)
  • [SavaOLED ERROR] — Критические ошибки (проблемы с I2C, отсутствие дисплея)
  • [SavaOLED WARN] — Предупреждения (переполнение буфера, некорректные параметры)

Когда использовать

Включайте отладку если:

  • Дисплей не инициализируется или не отображает изображение
  • Возникают проблемы с I2C подключением
  • Нужно отследить внутренние операции библиотеки
  • Разрабатываете новый функционал с использованием библиотеки

Важно: Отключайте debug-режим в финальной версии проекта для экономии памяти и повышения производительности.


13. Примеры использования

В папке examples/ находятся готовые примеры для быстрого старта:

Демонстрация графических возможностей библиотеки:

  • Анимация линий с разными режимами наложения
  • Круги с расширением от центра
  • Прямоугольники с заливкой и инверсией
  • Скругленные прямоугольники
  • Синусоидальные кривые
  • Физика отскакивающих мячей
  • Эффект загрузки в стиле ZX Spectrum
  • Работа с битмапами (drawBitmap)

Работа с текстом:

  • Вывод русского и английского текста
  • Горизонтальный скроллинг (StrScroll)
  • Автоматическое переключение режимов прокрутки
  • Использование семисегментного шрифта для температуры
  • Форматирование чисел с плавающей точкой
  • Разные режимы выравнивания (StrLeft, StrCenter, StrRight)

Интерактивное меню с кнопками:

  • Вертикальный скроллинг текста (drawPrintVert)
  • Навигация по меню с помощью кнопок
  • Обработка нажатий с антидребезгом
  • Визуальная обратная связь (мигание при выборе)
  • Комбинирование графических примитивов и текста
  • Использование режима INV_AUTO для инверсии текста на фоне

Пример использования (Скелет скетча)

#include <Arduino.h>
#include "SavaOLED_ESP32.h"
#include "Fonts/SF_Font_P8.h"

// 1. Конструктор
SavaOLED_ESP32 oled(128, 64);

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

    // 2. Инициализация
    oled.init(); // 400kHz, SDA=21, SCL=22
    oled.clear();
    oled.font(SF_Font_P8); // Установка шрифта
}

void loop() {
    // 3. Очистка буфера перед новым кадром
    oled.clear(); 
    
    // 4. Рисование примитивов
    oled.rectR(0, 0, 128, 64, 5, REPLACE, NO_FILL); // Рамка вокруг экрана
    
    // 5. Вывод текста
    oled.cursor(64, 20, StrCenter); // Курсор по центру
    oled.print("SavaOLED");
    oled.drawPrint(); // Отрисовка текста
    
    // 6. Вывод чисел
    oled.cursor(64, 40, StrCenter);
    oled.print(millis() / 1000); // Секунды
    oled.drawPrint();

    // 7. Отправка на экран
    oled.display(); 
}

About

OLED128x64 ssd1306 для ESP32

Topics

Resources

Stars

Watchers

Forks

Packages

 
 
 

Contributors