- 投稿日:2019-07-30T23:39:03+09:00
<br>を無いことにはできないか!?
HTMLのウェブページのアクセシビリティについて考えてて、ふと疑問に思ったことをメモしとく。
どうしても改行したい
ページの大きさの都合でどうしても強制改行したい場合がある。固有名なんかの途中でさえ改行したい場合がある。
春<br>日部
旧中<br>山道
これには大きな問題がある。
- 文節でもない箇所で区切られるので検索できない。
- 音声読み上げ式ユーザーエージェントはまるで正しく発音できない。
<br>
を無いことにはできないものか?実際にはそこにある
<br>
を無いことにはできないんだろか。検索性の問題はともかく、音声読み上げ式ユーザーエージェントで実際にはどうなるかはわからないど、悪あがきとしてこんなコードにを考えてみた。
春<br style="speak:none">日部旧中<br style="speak:none">山道…とはいっても、これで「はる・にちぶ」とか「きゅうなか・やまみち」とか読み上げられるのを回避できるような気はしないけど…。実際のところどうなんだろ。というか、視覚的レイアウト効果をもたらすだけで意味の句切れにも、音声読み上げの支障にもならないような、任意位置での強制改行はできるんだろか…。
- 投稿日:2019-07-30T16:49:09+09:00
【C#】メールをHTML形式で送信【OutLook】
概要
タイトルまんま。
OutLookをC#で動かして、HTML形式でメールを送る。
(ハイパーリンクを使いたかったのだが、検索してもなかなか使い方がわからなかった。検索の仕方が悪いのか…)
自分用の備忘録にコード
using System; using OutLook = Microsoft.Office.Interop.Outlook; namespace HTMLsample { class Program { static void Main(string[] args) { try { OutLook.Application outlookApp = new OutLook.Application(); OutLook.MailItem mail = outlookApp.CreateItem(OutLook.OlItemType.olMailItem) as OutLook.MailItem; mail.Subject = "HTMLsample"; OutLook.AddressEntry User = outlookApp.Session.CurrentUser.AddressEntry; String sHtml; sHtml = "<HTML>\n" + "<HEAD>\n" + "<TITLE>Sample GIF</TITLE>\n" + "</HEAD>\n" + "<BODY>\n" + "<p><a href=\"hogehoge\">foofoo</p>\n" + "</BODY>\n" + "</HTML>"; mail.HTMLBody = sHtml; mail.Recipients.Add("送信先"); mail.Recipients.ResolveAll(); mail.Send(); } catch { Console.WriteLine("メールは送信できませんでした"); } } } }以上。
テキストエディタでHTML書いて、それをコピペしても上手くいかなかった。
この書き方なら反映される。なぜ???
- 投稿日:2019-07-30T16:49:09+09:00
【C#】メールをHTML形式で送信【Outlook】
概要
タイトルまんま。
OutlookをC#で動かして、HTML形式でメールを送る。
(ハイパーリンクを使いたかったのだが、検索してもなかなか使い方がわからなかった。検索の仕方が悪いのか…)
自分用の備忘録にコード
using System; using Outlook = Microsoft.Office.Interop.Outlook; namespace HTMLsample { class Program { static void Main(string[] args) { try { Outlook.Application outlookApp = new Outlook.Application(); Outlook.MailItem mail = outlookApp.CreateItem(Outlook.OlItemType.olMailItem) as Outlook.MailItem; mail.Subject = "HTMLsample"; Outlook.AddressEntry User = outlookApp.Session.CurrentUser.AddressEntry; String sHtml; sHtml = "<HTML>\n" + "<HEAD>\n" + "<TITLE>Sample GIF</TITLE>\n" + "</HEAD>\n" + "<BODY>\n" + "<p><a href=\"hogehoge\">foofoo</a></p>\n" + "</BODY>\n" + "</HTML>"; mail.HTMLBody = sHtml; mail.Recipients.Add("送信先"); mail.Recipients.ResolveAll(); mail.Send(); } catch { Console.WriteLine("メールは送信できませんでした"); } } } }以上。
テキストエディタでHTML書いて、それをコピペしても上手くいかなかった。
この書き方なら反映される。なぜ???
- 投稿日:2019-07-30T14:37:15+09:00
【Webエンジニア目指している方向け・無料公開】HTML, CSS, JavaScriptが学べる解説動画129本をまとめました!
この2週間で撮ってきたHTML, CSS, JavaScript(基本文法, フロントエンド, バックエンド)の解説動画全てを以下の記事にまとめました^^
https://tsuyopon.xyz/learning-contents/web-dev-movie-list/
動画本数
動画リスト数と解説動画本数は以下のとおりです。
- 動画リスト : 21本
- 解説動画本数 : 129本
- 解説動画の合計時間 : 14時間20分45秒
一般公開している動画なので誰でも無料で観れます!
Webエンジニアを目指している方、参考にどうぞ!
- 投稿日:2019-07-30T14:37:15+09:00
【Webエンジニア目指している方向け】HTML, CSS, JavaScriptが学べる解説動画129本(14時間超え)をまとめました!
この2週間で撮ってきたHTML, CSS, JavaScript(基本文法, フロントエンド, バックエンド)の解説動画全てを以下の記事にまとめました^^
https://tsuyopon.xyz/learning-contents/web-dev-movie-list/
動画本数
動画リスト数と解説動画本数は以下のとおりです。
- 動画リスト : 21本
- 解説動画本数 : 129本
- 解説動画の合計時間 : 14時間20分45秒
一般公開している動画なので誰でも無料で観れます!
Webエンジニアを目指している方、参考にどうぞ!
- 投稿日:2019-07-30T13:44:15+09:00
コンソールからcontentEditable にする
以下をコンソールから打つべし
document.body.contentEditable = true;
- 投稿日:2019-07-30T13:04:16+09:00
あなたは守ってる?ロゴを使用する際の条件。
未来電子テクノロジーでインターンをしているerika_zです。
プログラミングの学習を始めて約1ヶ月半が経ちました。
HTMLとCSSで作成していた、簡易的な逆求人サイトがおおよそ完成しました。
少し修正して、GitHubにあげようとしている段階です!逆求人サイトでSNSの埋め込みをした際に驚いたことがありました。
それは、SNSのロゴを使用するにはたくさんの条件があることです。そこで今回は、主要なSNSのロゴを使用する際の条件について書きたいと思います。
はじめに
プログラミング初心者であるため、内容に誤りがあるかもしれません。
もし、誤りがあれば修正するのでどんどん指摘してください。SNSごとのロゴを使用する条件
・ロゴの色は青、または白(画像の上に配置する場合は必ず白)
・丸や四角で囲まれたロゴも使用できる。ただし、ロゴには囲みがないものが望ましい
・影をつけることは禁止
・ロゴの外枠だけでの使用は禁止
・ロゴにデザインを加えることは禁止・アイコンとして単体または他のソーシャルアイコンと一緒に使用でできる(サイズは隣接するアイコンと同じ大きさにする)
・「f」のロゴは、アニメーションで動かしてはいけない
・ロゴにデザインを加えることは禁止
・色は正しい青色または、白黒のみ<参照サイト>
https://ja.facebookbrand.com/assets/%E3%80%8Cf%E3%80%8D%E3%83%AD%E3%82%B4/?audience=landing・ロゴの4方向全部に、最低ロゴの半分の幅の空白部分を作る
・1辺の幅が29px以上
・ロゴを変形させることは禁止
・白のロゴの背景には、単色を使用する<参照サイト>
https://en.instagram-brand.com/assets/iconsまとめ
主要なSNSのロゴを使用する際の条件についてご紹介しました。
みなさんそれぞれの条件に合った方法で使用していましたか?
サイトを作る際には、SNSの埋め込みは必要な工程でしょう。
しかし、定められているルールは守る必要があります。
ご紹介できていない条件もありますし、それそれのSNSによって条件が異なるためしっかり確認しましょう。
- 投稿日:2019-07-30T08:59:23+09:00
【web開発 CSS】ドロップダウンメニュー4作品
はじめに
最近、ずっとほったらかしにしてきたCSSでデザインの勉強。webデザイナーになりたいわけではないので将来どこかweb系に就職して使うかは分からないが、最低限の知識はいるだろうと思って最近サンプルを作っている。入社すぐはフロントサイドとか担当させてもらった時とかに活用できそう。
今回4種類のホバー型ドロップダウンメニューを作成。左からItem1,2,3,4。以下コードの掲載と解説。まずItemの解説をする前にhtmlと共通のcssを以下に記載しておく。なお、こちらには今回の趣旨であるドロップダウンメニューに関するコードだけ記載している。全体のコードはGitHubにあげておく。
html<body> <ul class="sub2"> <li><a href="#">Sub-menu01</a></li> <li><a href="#">Sub-menu02</a></li> <li><a href="#">Sub-menu03</a></li> </ul> <ul class="sub3"> <li><a href="#">Sub-menu01</a></li> <li><a href="#">Sub-menu02</a></li> <li><a href="#">Sub-menu03</a></li> </ul> <div class="sub4_box"></div> <ul class="sub4"> <li><a href="#">Sub-menu01</a></li> <li><a href="#">Sub-menu02</a></li> <li><a href="#">Sub-menu03</a></li> </ul> <header> <section id="title">Title</section> <ul class="menu_bar"> <li class="menu_items"> item1 <div class="underbar"> <ul class="sub1"> <li><a href="#">Sub-menu01</a></li> <li><a href="#">Sub-menu02</a></li> <li><a href="#">Sub-menu03</a></li> </ul> </div> </li> <li class="menu_items"> item2 <div class="underbar"></div> </li> <li class="menu_items"> item3 <div class="underbar"></div> </li> <li class="menu_items"> item4 <div class="underbar"></div> </li> </ul> </header> </body>cssheader{ z-index: 3; /*下からmain,menu,headerの順番*/ position: relative; position: fixed; /*header固定*/ top: 0; left: 0; height: 120px; width: 100%; background-color: #696969; } ul{ z-index: 2; /*下からmain,menu,headerの順番*/ padding: 0; /*左のpadding削除*/ list-style: none; /*デフォルトのちょぼ消す*/ } a{ display: block; font-size: 0.8em; text-transform: uppercase; letter-spacing: .2em; } ul.menu_bar{ height: 40px; padding: 0; /*左のpadding削除*/ padding-top: 10px; margin: 0; display: flex; justify-content: center; /*アイコン、テキストボックスを横方向の中心に*/ }underbarクラス
これは各Itemの下に引いてあるアンダーバーであり、わざわざ各Itemとは別の要素で用意した。この理由は、Itemにborder-bottomを設定した場合にドロップダウンメニューとItemに隙間を開けないようにすると、Itemのborder-bottomとドロップダウンメニューの上部が重なり見栄えが悪くなるから。
Item1
こちらはシンプルなドロップダウンメニュー。メニューの幅はItem1の要素と同じで、表示するときに少し下がりながらfadeinしてくる感じ。
cssul.sub1{ position: absolute; visibility: hidden; /*hover前は非表示*/ opacity: 0; /*透明*/ top: 0; left: 0; width: 100%; background: white; transition: all 0.5s; -webkit-transition: all 0.5s; } ul.sub1 > li > a{ padding: 10px 10px; } .menu_items:nth-child(1):hover > div > ul.sub1{ top: 10px; visibility: visible; /*表示*/ opacity: 1; }hoverの謎
たぶんこれが一番王道のドロップダウンメニューかな?正直これを作っててなんでItemからhoverが外れてドロップダウンメニューにカーソルが写ってもドロップダウンメニューが消えないのかはよくわかっていない。どういう仕組みか分かる方いればご教授願います。
Item2
このドロップダウンメニューもよくwebサイトで見かけるような気がする。
cssul.sub2{ position: absolute; /*横幅をheaderと同じにするためheaderの子要素*/ display: flex; /*横並び*/ justify-content: center; /*アイコン、テキストボックスを横方向の中心に*/ width: 100%; height: 100px; top: 20px; /*画面外*/ left: 0; margin: 0; /*headerとsub2との隙間をなくす*/ background-color: white; } ul.sub2 > li > a{ padding: 30px 40px; }jquery$('header > ul > li:eq(1)').hover(function(){ //hoverした時 $('.sub2').css({ //transformの設定 'transform': 'translate3d(0, 100px, 0)', 'transition': 'all 0.2s', '-webkit-transition': 'all 0.2s' }); },function(){ //hoverが解除された時 //これはitem2からmouseleaveした直後の数回取得する $(':hover').each(function(){ //headerとitem2の間にundefinedがあるため if ($(this).attr('class') != undefined){ if ($(this).attr('class') != 'sub2'){ $('.sub2').css({'transform': 'translate3d(0, 0, 0)'}); } } }); }); $('.sub2').on('mouseleave',function(){ //transformの上書き $('.sub2').css({'transform': 'translate3d(0, 0, 0)'}); });jsとcssでのインデックスの扱い
Item1のcssで書いているように、要素を数えるときcssでは1から始まる。jsは0から始まる。
ドロップダウンメニューの収納
今回Item2,3,4で同じ手法でドロップダウンメニューを収納している。遠回りなやり方かもしれないが他に方法が思いつかなかったのでもっと調べる必要あり。具体的にはjqueryの$(':hover')のところであるが、カーソルがItemから外れた瞬間のカーソルがhoverしている要素がドロップダウンメニュー上であればそのまま表示、それ以外であれば収納、といった感じ。またドロップダウンメニューにはmouseleaveで収納するようになっているため、カーソルがItemかドロップダウンメニューから離れると収納する。
Item3
こちらの他と違う点はドロップダウンメニューの表示・収納時に他の画面も全て動く点。cssに関してはItem2の時と(クラス名がsub3と言う点以外)全く同じなので省略。
jquery$('header > ul > li:eq(2)').hover(function(){ //hoverした時 $('.sub3').css({ //transformの設定 'transform': 'translate3d(0, 100px, 0)', 'transition': 'all 0.2s', '-webkit-transition': 'all 0.2s' }); $('main').css({ //transformの設定 'transform': 'translate3d(0, 100px, 0)', 'transition': 'all 0.2s', '-webkit-transition': 'all 0.2s' }); },function(){ //hoverが解除された時 //これはitem3からmouseleaveした直後の数回取得する $(':hover').each(function(){ //headerとitem3の間にundefinedがあるため if ($(this).attr('class') != undefined){ if ($(this).attr('class') != 'sub3'){ $('.sub3').css({'transform': 'translate3d(0, 0, 0)'}); //mainが動くとz-indexが一番大きくなるため固定してitem1,2が後ろにいかないようにする $('main').css({ 'transform': 'translate3d(0, 0, 0)', 'z-index': '1' }); } } }); }); $('.sub3').on('mouseleave',function(){ //transformの上書き $('.sub3').css({'transform': 'translate3d(0, 0, 0)'}); $('main').css({ 'transform': 'translate3d(0, 0, 0)', 'z-index': '1' }); });z-indexの謎
ここでも正直なぜか理解していないことが発生。それはmainをtranslateで動かした後なぜかmainが画面最上部に表示されてしまうということ。よってz-indexを再設定しないと、Item3のドロップダウンメニューを表示させた後、Item1や2のドロップダウンメニューがmainの後ろ側にいってしまう。ドロップダウンメニューは動かしてもz-index変わらんのになぜ?これも分かる方いれば是非ご教授ください。
Item4
こちらはアニメーションを効かせて見栄えを工夫したもの。
cssul.sub4{ position: absolute; /*横幅をheaderと同じにするためheaderの子要素*/ display: flex; /*横並び*/ justify-content: center; /*アイコン、テキストボックスを横方向の中心に*/ width: 100%; height: 90px; /*sub4_boxよりも小さくすることでmouseleaveを正常に効かせる*/ top: 20px; /*画面外*/ left: 0; margin: 0; /*headerとsub2との隙間をなくす*/ } .sub4_box{ z-index: 2; /*下からmain,menu,headerの順番*/ position: absolute; /*横幅をheaderと同じにするためheaderの子要素*/ width: 100%; height: 100px; top: 20px; /*画面外*/ left: 0; background-color: white; } .sub4_box + ul > li > a{ padding: 30px 40px; }jquery$('header > ul > li:eq(3)').hover(function(){ //hoverした時 $('.sub4_box').css({ //transformの設定 'transform': 'translate3d(0, 100px, 0)', 'transition': 'all 0.2s', '-webkit-transition': 'all 0.2s' }); let time = 0.4; for (let i=0; i<3; i++){ $('body > ul:eq(2) > li:eq('+i+')').css({ //transformの設定 'transform': 'translate3d(0, 100px, 0)', 'transition': 'all '+time+'s', '-webkit-transition': 'all '+time+'s' }); time += 0.3; } },function(){ //hoverが解除された時 //これはitem2からmouseleaveした直後の数回取得する $(':hover').each(function(){ //headerとitem2の間にundefinedがあるため if ($(this).attr('class') != undefined){ if ($(this).attr('class') != 'sub4'){ for (let i=0; i<3; i++){ $('body > ul:eq(2) > li:eq('+i+')').css({ //transformの設定 'transform': 'translate3d(0, 0, 0)' }); } //listが動くのを待つため0.3秒遅らせる setTimeout(function(){ $('.sub4_box').css({'transform': 'translate3d(0, 0, 0)'}); },300); } } }); }); $('.sub4_box').on('mouseleave',function(){ //transformの上書き for (let i=0; i<3; i++){ $('body > ul:eq(2) > li:eq('+i+')').css({'transform': 'translate3d(0, 0, 0)'}); } setTimeout(function(){ $('.sub4_box').css({'transform': 'translate3d(0, 0, 0)'}); },300); });特に難しいことはしていない。強いて言うなら初めてsetTimeout()メソッド使ったくらい。
最後に
今回はドロップダウンメニューを作ってみた。ブロック要素の扱いにだいぶ慣れてきた気がする。あとはいろんなwebサイト参考にしておしゃれなデザインができるようになりたい。
- 投稿日:2019-07-30T06:39:07+09:00
python-cgiモジュールのFieldStorage型について
はじめに
pythonでwebページ間でデータをやりとりする為に
cgiモジュールを使った際に
調べたcgi.FieldStorage型について整理しました。Pythonの公式ドキュメントはこちら
https://docs.python.org/3/library/cgi.htmlpython-cgiについての記事はこちら
https://qiita.com/r_i_qita/items/82fb537688e0f2be4915cgi.FieldStorage
cgiモジュールで使用されているクラス。
FieldStorageクラスをインスタンス化することで、
クライアントから送られるフォームの内容を取得できるようになる。主な使い方
pythonファイルが実行された
<form></form>
に紐付くhtmlタグを取得する。例えば、以下のような記述を html と python に書いた場合、変数formの中身は次のようになる。
変数form(FieldStorage型)
(None, None, [MiniFieldStorage('hdn_name', 'hdn_value1'), MiniFieldStorage('hdn_name', 'hdn_value2'), MiniFieldStorage('text_name', 'text_value'), MiniFieldStorage('button', 'submit!!')])
# Python form = cgi.FieldStorage()<!-- HTML --> <form name="formSubmit" action="cgibin.py"> <input type="hidden" name="hdn_name" value="hdn_value1"> <input type="hidden" name="hdn_name" value="hdn_value2"> <input type="text" name="text_name" value="text_value"> <input type="submit" name="button" value="submit!!"> </form>データの取り出し方
上記のhtml, cgi.FiledStorageインスタンスを例に
フォームのデータを取り出す方法を以下に記載します。注釈
以降の説明では例の
name="hogename"に当たる部分をhtml_name,
value="hogevalue"に当たる部分をhtml_value と表現します。<!-- (例) --> <input type="hidden" name="hogename" value="hogevalue">form.getvalue(name)
指定したhtml_nameに紐付く、全てのhtml_valueを返す。
紐付くhtml_nameが
1つの場合はString型、複数ある場合はlist型で返す。# 例1) # 変数名:格納されるデータ:型 # ret:'text_value':<class str> ret = form.getvalue('text_name')# 例2) # 変数名:格納されるデータ:型 # ret:['hdn_value1', 'hdn_value2']:<class list> ret = form.getvalue('hdn_name')上記の通り、
html_nameの数によって、返ってくる型が異なるので、バグ懸念があります。
個人的には、後述のform.getlist()を使った方が良いと思います。form.getlist(name)
指定したhtml_nameに紐付く、全てのhtml_valueを返す。
紐付くhtml_nameの数に関わらず、list型で返す。# 例1) # 変数名:格納されるデータ:型 # ret:['text_value']:<class list> ret = form.getlist('text_name')# 例2) # 変数名:格納されるデータ:型 # ret:['hdn_value1', 'hdn_value2']:<class list> ret = form.getlist('hdn_name')form.list
pythonファイルが実行された
<form>
タグに紐付く、html_nameとhtml_valueをlistで返す。
listの要素はMiniFieldStorage型。
MiniFieldStorage型は、辞書型のように(key, value)形式でデータを持つ。# 例) # 変数名:格納されるデータ:型 # ret:[MiniFieldStorage('hdn_name', 'hdn_value1'), MiniFieldStorage('hdn_name', 'hdn_value2'), MiniFieldStorage('text_name', 'text_value'), MiniFieldStorage('button', 'submit!!')]:<class list> ret = form.listhtmlタグを全部取ってきて
XXXというhtml_valueがあれば処理1、
YYYというhtml_valueがあれば処理2 というような分岐処理に使える。form[name]
指定したhtml_nameとそれに紐付くhtml_valueをMiniFieldStorage型で返す。
紐付くhtml_nameが複数ある場合、list型 of MiniFieldStorage型で返す。# 例1) # 変数名:格納されるデータ:型 # ret:MiniFieldStorage('text_name', 'text_value'):<class MiniFieldStorage> ret = form['text_name']# 例2) # 変数名:格納されるデータ:型 # ret:[MiniFieldStorage('hdn_name', 'hdn_value1'), MiniFieldStorage('hdn_name', 'hdn_value2')]:<class list> ret = form['hdn_name']form[name].value
指定したhtml_nameに紐付く、html_valueをString型で返す。
ただし、紐付くhtml_nameが複数ある場合、
form[name]がlist型で返ってくる為、AttributeErrorが発生する。# 例1) # 変数名:格納されるデータ:型 # ret:'text_value':<class str> ret = form['text_name'].value# 例2) # raise AttributeError ret = form['hdn_name'].valueまとめ
以上のようにcgi.FieldStorageを使ってフォームの情報を取り出す事で
Webページ間のデータの受け渡しや動的なhtml処理などを簡単に行えます。
とても便利。
- 投稿日:2019-07-30T02:32:18+09:00
OV2640を使ってjpegデータ表示と設定変更(ESP32-WROVER-B)
概要
ESP32-WROVER-Bを使ってOV2640からjpegデータを取得してみました。ESP32-WROVER-Bでサーバーを立てて、ブラウザから各レジスタを制御できるようにしています。
コードは下記に公開しています。
https://github.com/koki-iwaizumi/esp32-ov2640-stream/tree/v1.0OV7725を使って同様のことをしています。
https://qiita.com/koki-iwaizumi/items/146b8b08f9146327f657公式
https://github.com/espressif/esp32-camera設定
esp-idfのフォルダ内でsubmoduleをクローンします。
git submodule add https://github.com/espressif/esp32-camera.git components/esp32-camera mv esp-idf/components/esp32-camera/driver/private_include/sccb.h esp-idf/components/esp32-camera/driver/include/sccb.h mv esp-idf/components/esp32-camera/driver/private_include/sensor.h esp-idf/components/esp32-camera/driver/include/sensor.hESP32(サーバー)
ESP32でサーバーを立てて、ローカルアドレスでアクセスできるようにしました。ブラウザで<ローカルアドレス>/へアクセスすると画像及び各レジスタの値が表示されます。
image,config0,config1,writeはajax及びXMLHttpRequestでサーバと通信しています。
- <ローカルアドレス>/ html
- <ローカルアドレス>/image image/jpegのバイナリデータ
- <ローカルアドレス>/config0 0xFF==0の場合のレジスタ情報のjson
- <ローカルアドレス>/config1 0xFF==1の場合のレジスタ情報のjson
- <ローカルアドレス>/write レジスタ変更
app_main.c#include <string.h> #include <esp_wifi.h> #include <esp_event_loop.h> #include <esp_log.h> #include <esp_system.h> #include <nvs_flash.h> #include <sys/param.h> #include <esp_http_server.h> #include <esp_camera.h> #include "mbedtls/base64.h" #include "cJSON.h" #include "sensor.h" #include "sccb.h" /********* io ***********/ #define CAM_PIN_PWDN 2 #define CAM_PIN_RESET 15 #define CAM_PIN_XCLK 27 #define CAM_PIN_SIOD 25 #define CAM_PIN_SIOC 23 #define CAM_PIN_VSYNC 22 #define CAM_PIN_HREF 26 #define CAM_PIN_PCLK 21 #define CAM_PIN_D9 19 #define CAM_PIN_D8 36 #define CAM_PIN_D7 18 #define CAM_PIN_D6 39 #define CAM_PIN_D5 5 #define CAM_PIN_D4 34 #define CAM_PIN_D3 32 #define CAM_PIN_D2 35 /********* wifi ***********/ #define EXAMPLE_WIFI_SSID "*" #define EXAMPLE_WIFI_PASS "*" static const char *TAG = "APP"; sensor_t* sensor = NULL; static const uint8_t regs_0[][1] = { {0x05}, {0x44}, {0x50}, {0x51}, {0x52}, {0x53}, {0x54}, {0x55}, {0x56}, {0x57}, {0x5A}, {0x5B}, {0x5C}, {0x7C}, {0x7D}, {0x86}, {0x87}, {0x8C}, {0xC0}, {0xC1}, {0xC2}, {0xC3}, {0xD3}, {0xDA}, {0xE0}, {0xED}, {0xF0}, {0xF7}, {0xF8}, {0xF9}, {0xFA}, {0xFB}, {0xFC}, {0xFD}, {0xFE}, {0xFF} }; static const uint8_t regs_1[][1] = { {0x00}, {0x03}, {0x04}, {0x08}, {0x09}, {0x0A}, {0x0B}, {0x0C}, {0x10}, {0x11}, {0x12}, {0x13}, {0x14}, {0x15}, {0x17}, {0x18}, {0x19}, {0x1A}, {0x1C}, {0x1D}, {0x24}, {0x25}, {0x26}, {0x2A}, {0x2B}, {0x2D}, {0x2E}, {0x2F}, {0x32}, {0x34}, {0x45}, {0x46}, {0x47}, {0x48}, {0x49}, {0x4B}, {0x4E}, {0x4F}, {0x50}, {0x5D}, {0x5E}, {0x5F}, {0x60}, {0x61}, {0x62} }; static camera_config_t camera_config = { .pin_pwdn = CAM_PIN_PWDN, .pin_reset = CAM_PIN_RESET, .pin_xclk = CAM_PIN_XCLK, .pin_sscb_sda = CAM_PIN_SIOD, .pin_sscb_scl = CAM_PIN_SIOC, .pin_d7 = CAM_PIN_D9, .pin_d6 = CAM_PIN_D8, .pin_d5 = CAM_PIN_D7, .pin_d4 = CAM_PIN_D6, .pin_d3 = CAM_PIN_D5, .pin_d2 = CAM_PIN_D4, .pin_d1 = CAM_PIN_D3, .pin_d0 = CAM_PIN_D2, .pin_vsync = CAM_PIN_VSYNC, .pin_href = CAM_PIN_HREF, .pin_pclk = CAM_PIN_PCLK, .xclk_freq_hz = 6000000, .ledc_timer = LEDC_TIMER_0, .ledc_channel = LEDC_CHANNEL_0, .pixel_format = PIXFORMAT_JPEG, .frame_size = FRAMESIZE_SVGA, //FRAMESIZE_UXGA .jpeg_quality = 12, .fb_count = 1 }; esp_err_t index_get_handler(httpd_req_t *req) { extern const unsigned char index_start[] asm("_binary_index_html_start"); extern const unsigned char index_end[] asm("_binary_index_html_end"); const size_t index_size = (index_end - index_start); httpd_resp_send_chunk(req, (const char *)index_start, index_size); httpd_resp_sendstr_chunk(req, NULL); return ESP_OK; } esp_err_t image_get_handler(httpd_req_t *req) { camera_fb_t *fb = esp_camera_fb_get(); if( ! fb){ ESP_LOGE(TAG, "Camera capture failed"); httpd_resp_send_500(req); return ESP_FAIL; } esp_err_t res = httpd_resp_set_type(req, "image/jpeg") || httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*") || httpd_resp_send(req, (const char *)fb->buf, fb->len); esp_camera_fb_return(fb); return res; } esp_err_t config0_get_handler(httpd_req_t *req) { esp_err_t res = SCCB_Write(sensor->slv_addr, 0xFF, 0); if(res != ESP_OK){ ESP_LOGE(TAG, "Failed SCCB_Write - 0xFF 0"); httpd_resp_send_500(req); return ESP_FAIL; } int data_json_size = 10 * 1000; char *data_json = calloc(data_json_size, sizeof(char)); if(data_json == NULL){ ESP_LOGE(TAG, "Failed to allocate frame buffer - data_json"); httpd_resp_send_500(req); return ESP_FAIL; } int regs_0_num = sizeof regs_0 / sizeof regs_0[0]; strcat(data_json, "{"); for(int i = 0; i < regs_0_num; i++){ char data_json_buf[255] = {'\0'}; sprintf(data_json_buf, "\"%d\":\"%d\"", regs_0[i][0], SCCB_Read(sensor->slv_addr, regs_0[i][0])); strcat(data_json, data_json_buf); if(i != regs_0_num - 1) strcat(data_json, ","); } strcat(data_json, "}"); ESP_LOGI(TAG, "data_json=%s", data_json); ESP_LOGI(TAG, "data_json length=%d", strlen((const char*)data_json)); res = httpd_resp_set_type(req, "application/json") || httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*") || httpd_resp_send(req, (const char *)data_json, strlen((const char*)data_json)); free(data_json); return res; } esp_err_t config1_get_handler(httpd_req_t *req) { esp_err_t res = SCCB_Write(sensor->slv_addr, 0xFF, 1); if(res != ESP_OK){ ESP_LOGE(TAG, "Failed SCCB_Write - 0xFF 1"); httpd_resp_send_500(req); return ESP_FAIL; } int data_json_size = 10 * 1000; char *data_json = calloc(data_json_size, sizeof(char)); if(data_json == NULL){ ESP_LOGE(TAG, "Failed to allocate frame buffer - data_json"); httpd_resp_send_500(req); return ESP_FAIL; } int regs_1_num = sizeof regs_1 / sizeof regs_1[0]; strcat(data_json, "{"); for(int i = 0; i < regs_1_num; i++){ char data_json_buf[255] = {'\0'}; sprintf(data_json_buf, "\"%d\":\"%d\"", regs_1[i][0], SCCB_Read(sensor->slv_addr, regs_1[i][0])); strcat(data_json, data_json_buf); if(i != regs_1_num - 1) strcat(data_json, ","); } strcat(data_json, "}"); ESP_LOGI(TAG, "data_json=%s", data_json); ESP_LOGI(TAG, "data_json length=%d", strlen((const char*)data_json)); res = httpd_resp_set_type(req, "application/json") || httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*") || httpd_resp_send(req, (const char *)data_json, strlen((const char*)data_json)); free(data_json); return res; } esp_err_t write_get_handler(httpd_req_t *req) { size_t buf_len = httpd_req_get_url_query_len(req) + 1; if(buf_len <= 1){ ESP_LOGE(TAG, "Failed buf_len"); httpd_resp_send_500(req); return ESP_FAIL; } char* buf = malloc(buf_len); if(httpd_req_get_url_query_str(req, buf, buf_len) != ESP_OK){ free(buf); ESP_LOGE(TAG, "Failed httpd_req_get_url_query_str"); httpd_resp_send_500(req); return ESP_FAIL; } char bank[4], key[4], value[4]; ESP_LOGI(TAG, "Found URL query => %s", buf); if(httpd_query_key_value(buf, "bank", bank, sizeof(bank)) != ESP_OK || httpd_query_key_value(buf, "key", key, sizeof(key)) != ESP_OK || httpd_query_key_value(buf, "value", value, sizeof(value)) != ESP_OK){ free(buf); ESP_LOGE(TAG, "Failed httpd_query_key_value"); httpd_resp_send_500(req); return ESP_FAIL; } free(buf); int key_i = atoi(key); int value_i = atoi(value); int bank_i = atoi(bank); ESP_LOGI(TAG, "Found URL query parameter => key=%s, value=%s, bank=%s", key, value, bank); ESP_LOGI(TAG, "Found URL query parameter => key=%d, value=%d, bank=%d", key_i, value_i, bank_i); if(SCCB_Write(sensor->slv_addr, 0xFF, bank_i) != ESP_OK || SCCB_Write(sensor->slv_addr, key_i, value_i) != ESP_OK){ ESP_LOGE(TAG, "Failed SCCB_Write"); httpd_resp_send_500(req); return ESP_FAIL; } const char *resp = "{\"status\":\"success\"}"; esp_err_t res = httpd_resp_set_type(req, "application/json") || httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*") || httpd_resp_send(req, resp, strlen(resp)); return res; } httpd_uri_t index_uri = { .uri = "/", .method = HTTP_GET, .handler = index_get_handler, }; httpd_uri_t image_uri = { .uri = "/image", .method = HTTP_GET, .handler = image_get_handler, }; httpd_uri_t config0_uri = { .uri = "/config0", .method = HTTP_GET, .handler = config0_get_handler, }; httpd_uri_t config1_uri = { .uri = "/config1", .method = HTTP_GET, .handler = config1_get_handler, }; httpd_uri_t write_uri = { .uri = "/write", .method = HTTP_GET, .handler = write_get_handler, }; httpd_handle_t start_webserver(void) { httpd_handle_t server = NULL; httpd_config_t config = HTTPD_DEFAULT_CONFIG(); ESP_LOGI(TAG, "Starting server on port: '%d'", config.server_port); if(httpd_start(&server, &config) == ESP_OK){ ESP_LOGI(TAG, "Registering URI handlers"); httpd_register_uri_handler(server, &index_uri); httpd_register_uri_handler(server, &image_uri); httpd_register_uri_handler(server, &config0_uri); httpd_register_uri_handler(server, &config1_uri); httpd_register_uri_handler(server, &write_uri); return server; } ESP_LOGI(TAG, "Error starting server!"); return NULL; } void stop_webserver(httpd_handle_t server) { httpd_stop(server); } static esp_err_t event_handler(void *ctx, system_event_t *event) { httpd_handle_t *server = (httpd_handle_t *) ctx; switch(event->event_id){ case SYSTEM_EVENT_STA_START: ESP_LOGI(TAG, "SYSTEM_EVENT_STA_START"); ESP_ERROR_CHECK(esp_wifi_connect()); break; case SYSTEM_EVENT_STA_GOT_IP: ESP_LOGI(TAG, "SYSTEM_EVENT_STA_GOT_IP"); ESP_LOGI(TAG, "Got IP: '%s'", ip4addr_ntoa(&event->event_info.got_ip.ip_info.ip)); if(*server == NULL){ *server = start_webserver(); } break; case SYSTEM_EVENT_STA_DISCONNECTED: ESP_LOGI(TAG, "SYSTEM_EVENT_STA_DISCONNECTED"); ESP_ERROR_CHECK(esp_wifi_connect()); if(*server){ stop_webserver(*server); *server = NULL; } break; default: break; } return ESP_OK; } static void initialise_wifi(void *arg) { tcpip_adapter_init(); ESP_ERROR_CHECK(esp_event_loop_init(event_handler, arg)); wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK(esp_wifi_init(&cfg)); ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM)); wifi_config_t wifi_config = { .sta = { .ssid = EXAMPLE_WIFI_SSID, .password = EXAMPLE_WIFI_PASS, }, }; ESP_LOGI(TAG, "Setting WiFi configuration SSID %s...", wifi_config.sta.ssid); ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config)); ESP_ERROR_CHECK(esp_wifi_start()); } void app_main() { static httpd_handle_t server = NULL; ESP_ERROR_CHECK(nvs_flash_init()); esp_err_t err = esp_camera_init(&camera_config); if(err != ESP_OK){ ESP_LOGE(TAG, "Camera Init Failed"); return; } sensor = esp_camera_sensor_get(); vTaskDelay(1000 / portTICK_RATE_MS); initialise_wifi(&server); }ブラウザ側
index.html<!doctype html> <html lang="ja"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.6.4/css/all.css"> <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous"> <title>OV2640</title> <style> .alert{ display:none; } #overlay{ position: fixed; top: 0; z-index: 100; width: 100%; height:100%; display: none; background: rgba(0,0,0,0.6); } .cv-spinner{ height: 100%; display: flex; justify-content: center; align-items: center; } .spinner{ width: 40px; height: 40px; border: 4px #ddd solid; border-top: 4px #2e93e6 solid; border-radius: 50%; animation: sp-anime 0.8s infinite linear; } @keyframes sp-anime{ 0% { transform: rotate(0deg); } 100% { transform: rotate(359deg); } } .is-hide{ display:none; } .ov_a{ height:600px; overflow-y:auto; } </style> </head> <body> <div id="overlay"> <div class="cv-spinner"> <span class="spinner"></span> </div> </div> <div class="text-center mt-3 mb-3"> <h1>OV2640</h1> </div> <div class="container-fluid"> <div class="row"> <div class="col-lg-6 text-center mb-4"> <div class="row"> <div class="col-md-12"> <div class="alert alert-success" role="alert"></div> <div class="alert alert-danger" role="alert"></div> </div> <div class="col-md-12"> <img id="img" class="mw-100"> </div> </div> </div> <div class="col-lg-6"> <div class="ov_a"> <div class="text-center mt-1 mb-1"> <span>0xFF=00</span> </div> <table id="table_regs0" class="table table-striped"> <tr> <th>Address(Hex)</th> <th>Name</th> <th>Value(Hex)</th> <th></th> </tr> </table> <div class="text-center mt-1 mb-1"> <span>0xFF=01</span> </div> <table id="table_regs1" class="table table-striped"> <tr> <th>Address(Hex)</th> <th>Name</th> <th>Value(Hex)</th> <th></th> </tr> </table> </div> </div> </div> </div> <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script> <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script> <script type="text/javascript"> $(function(){ var regs0 = { 0x05: "R_BYPASS", 0x44: "Qs", 0x50: "CTRLI", 0x51: "HSIZE", 0x52: "VSIZE", 0x53: "XOFFL", 0x54: "YOFFL", 0x55: "VHYX", 0x56: "DPRP", 0x57: "TEST", 0x5A: "ZMOW", 0x5B: "ZMOH", 0x5C: "ZMHH", 0x7C: "BRADDR", 0x7D: "BPDATA", 0x86: "CTRL2", 0x87: "CTRL3", 0x8C: "SIZEL", 0xC0: "HSIZE8", 0xC1: "VSIZE8", 0xC2: "CTRL0", 0xC3: "CTRL1", 0xD3: "R_DVP_SP", 0xDA: "IMAGE_MODE", 0xE0: "RESET", 0xED: "REGED", 0xF0: "MS_SP", 0xF7: "SS_ID", 0xF8: "SS_CTRL", 0xF9: "MC_BIST", 0xFA: "MC_AL", 0xFB: "MC_AH", 0xFC: "MC_D", 0xFD: "P_CMD", 0xFE: "P_STATUS", 0xFF: "RA_DLMT", }; var regs1 = { 0x0: "GAIN", 0x3: "COM1", 0x4: "REG04", 0x8: "REG08", 0x9: "COM2", 0xA: "PIDH", 0xB: "PIDL", 0xC: "COM3", 0xC: "COM3", 0x10: "AEC", 0x11: "CLKRC", 0x12: "COM7", 0x13: "COM8", 0x14: "COM9", 0x15: "COM10", 0x17: "HREFST", 0x18: "HREFEND", 0x19: "VSTART", 0x1A: "VEND", 0x1C: "MIDH", 0x1D: "MIDL", 0x24: "AEW", 0x25: "AEB", 0x26: "VV", 0x2A: "REG2A", 0x2B: "FRARL", 0x2D: "ADDVSL", 0x2E: "ADDVSH", 0x2E: "ADDVSH", 0x2F: "YAVG", 0x32: "REG32", 0x34: "ARCOM2", 0x45: "REG45", 0x46: "FLL", 0x47: "FLH", 0x48: "COM19", 0x49: "ZOOMS", 0x4B: "COM22", 0x4E: "COM25", 0x4F: "BD50", 0x50: "BD60", 0x5D: "REG5D", 0x5E: "REG5E", 0x5F: "REG5F", 0x60: "REG60", 0x61: "HISTO_LOW", 0x62: "HISTO_HIGH", }; function get_image(){ var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function(){ if(this.readyState == 4 && this.status == 200){ var img = document.getElementById("img"); img.src = window.URL.createObjectURL(this.response); get_image(); } } xhr.open('GET', "./image"); xhr.responseType = 'blob'; xhr.send(); }; function write_config(bank, k, v){ $.ajax({ url: "./write", type: "GET", dataType: "json", data: { bank: bank, key: k, value: v }, beforeSend: function() { $(".alert").css("display", "none"); } }) .done((data) => { $.each(data, function(i, item){ if(item == "success"){ $(".alert-success").css("display", "block"); $(".alert-success").text('bank:' + bank + ' key:0x' + Number(k).toString(16) + ' value:0x' + Number(v).toString(16) + ' success'); }else{ $(".alert-danger").css("display", "block"); $(".alert-danger").text('bank:' + bank + ' key:0x' + Number(k).toString(16) + ' value:0x' + Number(v).toString(16) + ' failed'); } }); }) .fail((data) => { $(".alert-danger").css("display", "block"); $(".alert-danger").text('bank:' + bank + ' key:0x' + Number(k).toString(16) + ' value:0x' + Number(v).toString(16) + ' failed'); }); }; $(document).ready(function(){ $.ajax({ url: "./config0", type: "GET", dataType: "json", async: false, }) .done((data) => { $.each(data, function(i, item){ $("#table_regs0").append('<tr><td>0x' + Number(i).toString(16) + '</td><td>' + regs0[i] + '</td><td id="value_0_' + i + '">0x' + Number(item).toString(16) + '(0b' + Number(item).toString(2) + ')</td><td><input type="range" class="w-100 slider0" name="' + i + '" value="' + item + '" min="0" max="255" step="1"></td></tr>'); }); }); $.ajax({ url: "./config1", type: "GET", dataType: "json", async: false, }) .done((data) => { $.each(data, function(i, item){ $("#table_regs1").append('<tr><td>0x' + Number(i).toString(16) + '</td><td>' + regs1[i] + '</td><td id="value_1_' + i + '">0x' + Number(item).toString(16) + '(0b' + Number(item).toString(2) + ')</td><td><input type="range" class="w-100 slider1" name="' + i + '" value="' + item + '" min="0" max="255" step="1"></td></tr>'); }); }); get_image(); $(".slider0").on("input change", function(){ $("#value_0_" + $(this).attr("name")).text("0x" + Number($(this).val()).toString(16) + "(0b" + Number($(this).val()).toString(2) + ")"); }); $(".slider1").on("input change", function(){ $("#value_1_" + $(this).attr("name")).text("0x" + Number($(this).val()).toString(16) + "(0b" + Number($(this).val()).toString(2) + ")"); }); $(".slider0").on("change", function(){ write_config(0, $(this).attr("name"), $(this).val()) }); $(".slider1").on("change", function(){ write_config(1, $(this).attr("name"), $(this).val()) }); }); }); </script> </body> </html>これから
・50Hzのバンドフィルターが上手く動作していないため、原因を探る
・色彩が少し淡いので、もう少しはっきりさせたい
・Qsの限界値を探る
・暗闇時の赤外線を当てた場合