Windowsアプリケーション(C#)からarduino の 1.5inch RGB OLED Module に画像を送る
前記事の結果を使います
やること
- アプリ側
- 画像を読み込む.
- 画像から128x128の領域を選択する
- 128x128の領域の画像をarduinoにシリアルを通して送る
- arduino側
- OLEDのセットアップ
- 受け取った画像を表示する
code
csharp側
Form1.cs
を載せます.デザイナーで作成したコンポーネント等の名前はデフォルトのままです.
Form1
コンストラクタでは,通信ポートを SerialPort.GetPortNames()
から適当に選んでいます.
arduinoと通信するメソッドは CropAndSendSerial
です.イベント処理中にやっているので,おそらく連打に弱いです.
CropAndSendSerial
メソッドでは,選択した128x128の領域のビットマップを作成,ビットマップから通信バッファ buff
を作成して,シリアルに送ります.
特に何も加工せず,128x128px24bitカラーの画像をarduinoに送りつけます.
using System; using System.Drawing; using System.IO.Ports; using System.Windows.Forms; namespace ImgArduino { public partial class Form1 : Form { Image image = null; Bitmap cropped = null; Rectangle selection = new Rectangle(0, 0, 128, 128); public Form1() { InitializeComponent(); var portNames = SerialPort.GetPortNames(); if (portNames.Length == 0) throw new Exception("ポートが無い"); serialPort1.PortName = portNames[0]; } // private void CropAndSendSerial() { if (image == null) return; if (cropped == null) cropped = new Bitmap(128, 128); using (var g = Graphics.FromImage(cropped)) { g.DrawImageUnscaled(image, -selection.X, -selection.Y); } byte[] buff = new byte[128 * 128 * 3]; for (int i = 0; i < 128; ++i) { for (int j = 0; j < 128; ++j) { buff[(i * 128 + j) * 3 + 0] = (byte)(cropped.GetPixel(j, i).R); buff[(i * 128 + j) * 3 + 1] = (byte)(cropped.GetPixel(j, i).G); buff[(i * 128 + j) * 3 + 2] = (byte)(cropped.GetPixel(j, i).B); } } serialPort1.Open(); serialPort1.Write(buff, 0, 128 * 128 * 3); serialPort1.Close(); } // private void Form1_Paint(object sender, PaintEventArgs e) { if (image != null) e.Graphics.DrawImageUnscaled(image, new Point(0, 0)); e.Graphics.DrawRectangle(Pens.Red, selection); } private void 画像選択OToolStripMenuItem_Click(object sender, EventArgs e) { var res = openFileDialog1.ShowDialog(); if (res != DialogResult.OK) return; var fileName = openFileDialog1.FileName; image = Image.FromFile(fileName); Invalidate(); } // Point pointLast = Point.Empty; private void Form1_MouseDown(object sender, MouseEventArgs e) { pointLast = new Point(e.X, e.Y); } private void Form1_MouseMove(object sender, MouseEventArgs e) { if (pointLast == Point.Empty) return; selection.X += e.X - pointLast.X; selection.Y += e.Y - pointLast.Y; if (selection.X < 0) selection.X = 0; if (selection.Y < 0) selection.Y = 0; pointLast = new Point(e.X, e.Y); Invalidate(); } private void Form1_MouseUp(object sender, MouseEventArgs e) { pointLast = Point.Empty; CropAndSendSerial(); } private void Form1_MouseLeave(object sender, EventArgs e) { pointLast = Point.Empty; } } }
arduino側
arduinoのOLEDドライバ部分(setupとloop以外)のコードを載せます.前記事と同じなので説明は省略.
#include "SPI.h" #define SSD1351_WIDTH 128 #define SSD1351_HEIGHT 128 #define SSD1351_CMD_SETCOLUMN 0x15 #define SSD1351_CMD_SETROW 0x75 #define SSD1351_CMD_WRITERAM 0x5C #define SSD1351_CMD_READRAM 0x5D #define SSD1351_CMD_SETREMAP 0xA0 #define SSD1351_CMD_STARTLINE 0xA1 #define SSD1351_CMD_DISPLAYOFFSET 0xA2 #define SSD1351_CMD_DISPLAYALLOFF 0xA4 #define SSD1351_CMD_DISPLAYALLON 0xA5 #define SSD1351_CMD_NORMALDISPLAY 0xA6 #define SSD1351_CMD_INVERTDISPLAY 0xA7 #define SSD1351_CMD_FUNCTIONSELECT 0xAB #define SSD1351_CMD_DISPLAYOFF 0xAE #define SSD1351_CMD_DISPLAYON 0xAF #define SSD1351_CMD_PRECHARGE 0xB1 #define SSD1351_CMD_DISPLAYENHANCE 0xB2 #define SSD1351_CMD_CLOCKDIV 0xB3 #define SSD1351_CMD_SETVSL 0xB4 #define SSD1351_CMD_SETGPIO 0xB5 #define SSD1351_CMD_PRECHARGE2 0xB6 #define SSD1351_CMD_SETGRAY 0xB8 #define SSD1351_CMD_USELUT 0xB9 #define SSD1351_CMD_PRECHARGELEVEL 0xBB #define SSD1351_CMD_VCOMH 0xBE #define SSD1351_CMD_CONTRASTABC 0xC1 #define SSD1351_CMD_CONTRASTMASTER 0xC7 #define SSD1351_CMD_MUXRATIO 0xCA #define SSD1351_CMD_COMMANDLOCK 0xFD #define SSD1351_CMD_HORIZSCROLL 0x96 #define SSD1351_CMD_STOPSCROLL 0x9E #define SSD1351_CMD_STARTSCROLL 0x9F namespace OLED{ const uint8_t oled_cs = 10; const uint8_t oled_rst = 8; const uint8_t oled_dc = 7; void update_cs(uint8_t x) { digitalWrite(oled_cs, x); } void update_rst(uint8_t x) { digitalWrite(oled_rst, x); } void update_dc(uint8_t x) { digitalWrite(oled_dc, x); } void write_command(uint8_t cmd) { update_dc(LOW); SPI.transfer(cmd); update_dc(HIGH); } void write_data(uint8_t dat) { update_dc(HIGH); SPI.transfer(dat); update_dc(LOW); } void ram_address(){ // ? write_command(SSD1351_CMD_SETCOLUMN); write_data(0x00); write_data(0x7f); write_command(SSD1351_CMD_SETROW); write_data(0x00); write_data(0x7f); } void init(void) { pinMode(oled_cs, OUTPUT); pinMode(oled_rst, OUTPUT); pinMode(oled_dc, OUTPUT); SPI.setDataMode(SPI_MODE0); SPI.setBitOrder(MSBFIRST); SPI.setClockDivider(SPI_CLOCK_DIV2); SPI.begin(); update_cs(LOW); update_rst(LOW); delay(500); update_rst(HIGH); delay(500); write_command(SSD1351_CMD_COMMANDLOCK); write_data(0x12); write_command(SSD1351_CMD_COMMANDLOCK); write_data(0xB1); write_command(SSD1351_CMD_DISPLAYOFF); write_command(SSD1351_CMD_DISPLAYALLOFF); // Normal Display mode ram_address(); write_command(SSD1351_CMD_CLOCKDIV); write_data(0xF1); write_command(SSD1351_CMD_MUXRATIO); write_data(0x7F); write_command(SSD1351_CMD_SETREMAP); //set re-map & data format //write_data(0x74); //Horizontal address increment write_data(0xb4); write_command(SSD1351_CMD_STARTLINE); //set display start line write_data(0x00); //start 00 line write_command(SSD1351_CMD_DISPLAYOFFSET); //set display offset write_data(0x00); write_command(SSD1351_CMD_FUNCTIONSELECT); write_data(0x01); // 元のOLED_Driver.cppではwrite_commandだがwrite_dataの間違い? write_command(SSD1351_CMD_SETVSL); write_data(0xA0); write_data(0xB5); write_data(0x55); write_command(SSD1351_CMD_CONTRASTABC); write_data(0xC8); write_data(0x80); write_data(0xC0); write_command(SSD1351_CMD_CONTRASTMASTER); write_data(0x0F); write_command(SSD1351_CMD_PRECHARGE); write_data(0x32); write_command(SSD1351_CMD_DISPLAYENHANCE); write_data(0xA4); write_data(0x00); write_data(0x00); write_command(SSD1351_CMD_PRECHARGELEVEL); write_data(0x17); write_command(SSD1351_CMD_PRECHARGE2); write_data(0x01); write_command(SSD1351_CMD_VCOMH); write_data(0x05); write_command(SSD1351_CMD_NORMALDISPLAY); // Clear_Screen(); write_command(SSD1351_CMD_DISPLAYON); //display on } void set_coordinate(uint16_t x, uint16_t y) { if ((x >= SSD1351_WIDTH) || (y >= SSD1351_HEIGHT)) return; //Set x and y coordinate write_command(SSD1351_CMD_SETCOLUMN); write_data(x); write_data(SSD1351_WIDTH-1); write_command(SSD1351_CMD_SETROW); write_data(y); write_data(SSD1351_HEIGHT-1); write_command(SSD1351_CMD_WRITERAM); } }
setup
,loop
部分です.
OLED::ram_address();
で描画範囲の指定(全画面指定),
OLED::write_command(SSD1351_CMD_WRITERAM);
でOLEDを書き込みモードにします.
シリアルから画素データが届きますが,これは1色8bitのデータなので,1色6bitのデータに変換するためにシフトしています.
void setup() { OLED::init(); Serial.begin(9600); OLED::ram_address(); OLED::write_command(SSD1351_CMD_WRITERAM); } const uint16_t wh = 128*128*3; uint16_t idx = 0; void loop() { while (Serial.available() > 0) { int val = Serial.read(); OLED::write_data(val >> 2); if (++idx >= wh) { // 終端画素に到達したら先頭へ戻る idx = 0; OLED::ram_address(); OLED::write_command(SSD1351_CMD_WRITERAM); } } }