DMX512 Streaming Recorder & RDM Interface – A Concept

This USB device works with one DMX universe. A DMX universe is 512 channels of 256 levels. The device also has a slot for a memory card. Maximum memory card size is 32GB.

The device also works as a streaming recorder. When show is running you can simultaneously save it to memory card and output to DMX512 port. If USB connection is lost during play it rewinds automatically and starts from beginning of saved show.

With 2GB memory card maximum recording time is 4 days! Record is simple series of CSV text files on FAT file system. Starting from first subdirectory with 1.csv and so on. Each subdirectory is separate show.

Changing operating mode and choosing between saved shows can be done with onboard display and button. Display shows simple to understand symbols for recording and playback functions and numbers for choosing between memory slots. By pressing button shortly it blinks current symbol and it is possible to browse through functions. Long press chooses that function and it’s symbol stops blinking.

The device also works as a RDM interface. With compatible software on computer it allows control and monitor RDM fixtures remotely. Currently on Linux there is no any software available.

AVR Program

The device uses AtMega328PB-AUR microcontroller with 16MHz crystal. It includes two hardware serial ports and two hardware SPI port. First serial port is USB interface and second serial port is DMX/RDM interface. Speed on both serial ports must be 250000 baud. First SPI port is used only for programming and second SPI port is used to communicate with memory card. Program is written with C++ language. Microcontroller is programmed using avr-gcc and avrdude. Program is released under GNU General Public License.

This program uses:

  • Enttec DMX USB Pro protocol to communicate with PC.
    API specification: https://www.enttec.com/products/controls/usb/2-universe-usb-computer-interface-dmx/
    Copyright (c) 2007 Enttec Pty/Ltd
  • Parts from DMXSerial and DMXSerial2 libraries.
    Copyright (c) 2011-2013 by Matthias Hertel, http://www.mathertel.de
    This work is licensed under a BSD style license. See http://www.mathertel.de/License.aspx
  • Parts from SdFat and Sd2Card libraries.
    Copyright (c) 2009 by William Greiman
    GNU General Public License V3.
  • Parts from Arduino SD-library.
    Copyright (c) 2010 SparkFun Electronics
    GNU General Public License V3.
  • CRC-7 routine and CRC-7 table from
    https://github.com/signal11/m-stack/blob/master/storage/src/crc.c
    Copyright (c) 2013 Alan Ott
    Copyright (c) 2013 Signal 11 Software
    GNU Lesser General Public License V3.

AVR/Arduino RFID Reader

This simple reader is based on previous post circuit and code. LCD display and custom AVR/Arduino circuit is added. Tag number is displayed on screen when detected. Detecting distance is about 2-3cm. Working voltage is 4.5V. This is maximum voltage of LCD display.

Video: https://www.youtube.com/watch?v=PWo2UU3oikA

Modified code with LCD driving:

#include <LiquidCrystal.h>

uint8_t cparity;
uint8_t numbers[5];

LiquidCrystal lcd(4,2,5,6,8,9);

void setup() {
  //Serial.begin(115200);
  pinMode(11, OUTPUT); // PWM output
  pinMode(13, OUTPUT); // Status LED

  lcd.begin(8,2);
  lcd.print("RFID");
  lcd.setCursor(0,1);
  lcd.print("reader");

  TCCR2A = 0;
  TCCR2B = 0;
  TCNT2  = 0;
  TCCR2A = 0b01000001;
  TCCR2B = 0b00001001;
  OCR2A  = 32; // 32=125kHz, 27=143kHz == (16000000/(2*1*143000))/2
}

void wait(uint8_t t){
  uint8_t counter = 0,last = 0,next;
  do {
    next = PINB & (1 << 3);
    if (next != last) counter++;
    last = next;
  } while (counter < t);
}

uint8_t readBit(uint8_t time1, uint8_t time2) {
  
  wait(time1);
  
  uint8_t counter = 0, last = 0, state1 = PIND & (1 << 7), state2;
  do {
    uint8_t next = PINB & (1 << 3);
    if (next != last) counter++;
    last = next;
    state2 = PIND & (1 << 7);
  } while (counter <= time2 && state1 == state2);
  
  if (state1 == state2) return 2;
  if (state2 == 0) return 0; else return 1;
}

int8_t read4Bits(uint8_t time1, uint8_t time2) {
  
  uint8_t number = 0, parity = 0;
  for (uint8_t i = 4; i > 0; i--) {
    uint8_t bit1 = readBit(time1, time2);
    if (bit1 == 2) return -1;
    number = (number << 1) + bit1;
    parity += bit1;
  }
  
  uint8_t bit1 = readBit(time1, time2);
  if (bit1 == 2 || (parity & 1) != bit1) return -1;
  cparity ^= number;
  return number;
}

uint8_t readCard() {
  loop_until_bit_is_clear(PIND, 7);
  loop_until_bit_is_set(PIND, 7);
  
  uint8_t counter = 0, last = 0, next;  
  do {
    next = PINB & (1 << 3);
    if (next != last) counter++;
    last = next;
  } while (bit_is_set(PIND, 7) && counter<0xFF);
  
  if (counter == 0xFF && bit_is_set(PIND, 7)) return 1;
  
  uint8_t halfbit = counter, offset = counter >> 1;  
  if (readBit(offset, halfbit) != 1) return 1;
  
  for (uint8_t i = 7; i > 0; i--)
    if (readBit(halfbit + offset, halfbit) != 1) return 1;
    
  cparity=0;
  for (uint8_t i = 0; i < 5; i++) {
    int8_t n1 = read4Bits(halfbit + offset, halfbit),
           n2 = read4Bits(halfbit + offset, halfbit);
    if (n1 < 0 || n2 < 0) return 1;
    numbers[i] = (n1 << 4) + n2;
  }
  
  uint8_t cp = 0;
  for (uint8_t i = 4; i > 0; i--) {
    uint8_t bit1 = readBit(halfbit + offset, halfbit);
    if (bit1 == 2) return 1;
    cp = (cp << 1) + bit1;
  }
  
  if (cparity != cp) return 1;
  if (readBit(halfbit + offset, halfbit) != 0) return 1;
  
  return 0;
}

void loop() {

  uint8_t result;
  do {
    result = readCard();
    PORTB ^= (1<<5); // LED drive
  } while (result != 0);

  lcd.clear();
  lcd.print("Raw data");
  lcd.setCursor(0,1);
  //Serial.print("Raw data: ");
  for (uint8_t i=0;i<5;i++) lcd.print(numbers[i],HEX); //Serial.print(numbers[i],HEX);
  //Serial.println();

  delay(1000);
  lcd.clear();
  lcd.print("Number");
  lcd.setCursor(0,1);

  //Serial.print("Card number: ");
  uint8_t numbersr[4];
  numbersr[0]=numbers[4];
  numbersr[1]=numbers[3];
  numbersr[2]=numbers[2];
  numbersr[3]=numbers[1];
  //Serial.print(*(uint32_t*)(&numbersr),DEC);
  //Serial.println();
  lcd.print(*(uint32_t*)(&numbersr),DEC);
  
  delay(1000);
}

Part list:

  • R1, R9, R10 – 1k resistor
  • R2 – 47k resistor
  • R3, R6, R7, R8 – 22k resistor
  • R4, R5 – 2k2 resistor
  • R11 – 680 resistor
  • R12 – 10k resistor trimmer
  • C1 – 4n7 capacitor
  • C2 – 2nF capacitor
  • C3, C4, C6, C9 – 100nF capacitor
  • C5 – 47pF capacitor
  • C7, C8 – 18pF capacitor
  • D1, D2, D3 – 1N4148 diode
  • D4 – Green LED diode
  • Y1 – 16MHz crystal
  • Q1 – 2N3904 transistor
  • Q2 – 2N3906 transistor
  • U1 – LM324 operational amplifier ic
  • IC1 – AtMega328P microcontroller ic

L1 is not normal component. Instead it is hand wound coil with inductance of 375µH. It can be physically any size or shape. Only important thing is inductance. Online calculator is good tool for this. Calculator asks permeability for air in this case. It is 1.

LCD connection:

  1. GND
  2. VCC
  3. V5 – (CONTR)
  4. RS – (D4)
  5. RW – (GND)
  6. E – (D2)
  7. DB0 – (NC = No Connection)
  8. DB1 – (NC)
  9. DB2 – (NC)
  10. DB3 – (NC)
  11. DB4 – (D5)
  12. DB5 – (D6)
  13. DB6 – (B0)
  14. DB7 – (B1)

Schematics:

Simple Arduino RFID reader

This simple circuit does not use any external modules to interfacing RFID reader to Arduino. The code below generates 125kHz frequency to other end of circuit. Then the other end sends the received data to Arduino input.

Inductor must be 375µH, but can be any size physically. Good reference to calculate correct value is here: https://www.allaboutcircuits.com/tools/coil-inductance-calculator/. I personally used 46 turns 58mm diameter coil of 0.5mm normal interconnecting wire. I used an empty can of Red Bull energy drink as former. Coil diameter is measured in the center of coil ‘edges’. Detecting distance is about 2-3cm.

Arduino program sends RFID tag data to serial port. This can then be used to for example open an electric lock. 125kHz output is pin 11 and data input is pin 7.

uint8_t cparity;
uint8_t numbers[5];

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

  pinMode(11, OUTPUT); // PWM output

  TCCR2A = 0;
  TCCR2B = 0;
  TCNT2  = 0;
  TCCR2A = 0b01000001;
  TCCR2B = 0b00001001;
  OCR2A  = 32; // 32=125kHz, 27=143kHz == (16000000/(2*1*143000))/2
}

void wait(uint8_t t){
  uint8_t counter = 0,last = 0,next;
  do {
    next = PINB & (1 << 3);
    if (next != last) counter++;
    last = next;
  } while (counter < t);
}

uint8_t readBit(uint8_t time1, uint8_t time2) {
  
  wait(time1);
  
  uint8_t counter = 0, last = 0, state1 = PIND & (1 << 7), state2;
  do {
    uint8_t next = PINB & (1 << 3);
    if (next != last) counter++;
    last = next;
    state2 = PIND & (1 << 7);
  } while (counter <= time2 && state1 == state2);
  
  if (state1 == state2) return 2;
  if (state2 == 0) return 0; else return 1;
}

int8_t read4Bits(uint8_t time1, uint8_t time2) {
  
  uint8_t number = 0, parity = 0;
  for (uint8_t i = 4; i > 0; i--) {
    uint8_t bit1 = readBit(time1, time2);
    if (bit1 == 2) return -1;
    number = (number << 1) + bit1;
    parity += bit1;
  }
  
  uint8_t bit1 = readBit(time1, time2);
  if (bit1 == 2 || (parity & 1) != bit1) return -1;
  cparity ^= number;
  return number;
}

uint8_t readCard() {
  loop_until_bit_is_clear(PIND, 7);
  loop_until_bit_is_set(PIND, 7);
  
  uint8_t counter = 0, last = 0, next;  
  do {
    next = PINB & (1 << 3);
    if (next != last) counter++;
    last = next;
  } while (bit_is_set(PIND, 7) && counter<0xFF);
  
  if (counter == 0xFF && bit_is_set(PIND, 7)) return 1;
  
  uint8_t halfbit = counter, offset = counter >> 1;  
  if (readBit(offset, halfbit) != 1) return 1;
  
  for (uint8_t i = 7; i > 0; i--)
    if (readBit(halfbit + offset, halfbit) != 1) return 1;
    
  cparity=0;
  for (uint8_t i = 0; i < 5; i++) {
    int8_t n1 = read4Bits(halfbit + offset, halfbit),
           n2 = read4Bits(halfbit + offset, halfbit);
    if (n1 < 0 || n2 < 0) return 1;
    numbers[i] = (n1 << 4) + n2;
  }
  
  uint8_t cp = 0;
  for (uint8_t i = 4; i > 0; i--) {
    uint8_t bit1 = readBit(halfbit + offset, halfbit);
    if (bit1 == 2) return 1;
    cp = (cp << 1) + bit1;
  }
  
  if (cparity != cp) return 1;
  if (readBit(halfbit + offset, halfbit) != 0) return 1;
  
  return 0;
}

void loop() {

  uint8_t result;
  do {
    result = readCard();
  } while (result != 0);

  Serial.print("Raw data: ");
  for (uint8_t i=0;i<5;i++) Serial.print(numbers[i],HEX);
  Serial.println();

  Serial.print("Card number: ");
  uint8_t numbersr[4];
  numbersr[0]=numbers[4];
  numbersr[1]=numbers[3];
  numbersr[2]=numbers[2];
  numbersr[3]=numbers[1];
  Serial.print(*(uint32_t*)(&numbersr),DEC);
  Serial.println();

  delay(1000);
}

References:

OpenDMX Compatible Unbuffered USB-DMX Interface (part 2)

First prototype is coming in October, 2018.

UPDATE Oct. 20. Now it’s ready and it’s working. Tested with QLC+ and with three channel DMX receiver. It is however impractical large and heavy to connect directly in to USB port. So next version fixes this. Coming soon.

UPDATE Oct. 12. Almost ready.

Open DMX USB is an open USB to DMX hardware design developed by Enttec. The Open in Open DMX USB refers to the fact that everybody is free to use the design and produce its own USB DMX dongle without paying any licenses.

The Open DMX USB is inexpensive way to get into the world of controlling DMX devices from a PC. It relies on the computer for all calculations and timing of the DMX signal. It is ideal for small live music and theater applications.

Powered by a computer over USB. Standard 3pin DMX port. Compatible with a wide range of 3rd Party software. Control up to 512 channels. Plugged directly on to the USB port.

This DMX USB interface is based on the FTDI 232RL chip, it’s a USB to serial converter. It’s designed accordingly to the USB specification and will enter suspend mode when there is no activity on the bus. It then shutdown the device and draw no more than rated suspend current.

Using a simple application on a PC you can send and receive DMX512. Recommended application is QLC+ because it is a free and cross-platform software to control DMX or analog lighting systems like moving heads, dimmers, scanners etc. QLC+ runs on Windows, MacOS and Linux.

D2XX drivers. https://www.ftdichip.com/Drivers/D2XX.htm

List of compatible control software and programming examples. https://www.enttec.com/products/controls/usb/open-dmx-usb/

Reference schematics for DMX bus termination. http://www.mathertel.de/Arduino/DMXShield.aspx

DMX 3pin XLR pinout reference. https://en.wikipedia.org/wiki/DMX512#XLR-3_pinout

Text sources:

Dual DC motor controller with Arduino (part 2)

Now with wireless control using 433MHz frequency. Code uses RadioHead-library.

Transmitter Arduino code:

#include <RH_ASK.h>
#include <SPI.h> // Not actualy used but needed to compile

RH_ASK driver(2000, 2, A5, 4);

byte old_mot_1;
byte old_mot_2;

void setup() {
  driver.init();
}

void loop() {
  int pot_1 = analogRead(1); // potentiometers values
  int pot_2 = analogRead(0);

  byte mot_1 = map(pot_1, 0, 1023, 0, 255);
  byte mot_2 = map(pot_2, 0, 1023, 0, 255);

  byte msg[3];
  msg[0] = mot_1;
  msg[1] = mot_2;
  driver.send(msg, 3);
  driver.waitPacketSent();
  delay(200);
}

Receiver Arduino code:

#include <RH_ASK.h>
#include <SPI.h> // Not actualy used but needed to compile

RH_ASK driver(2000, 13, 2, 4);

int pot_1 = 128;
int pot_2 = 128;

void setup() {
  driver.init();
  pinMode(7, OUTPUT);
  pinMode(8, OUTPUT);
}

void loop() {
  byte buf[3];
  if (driver.recv(buf, 3)) { // Non-blocking
    pot_1 = buf[0]; // potentiometers values
    pot_2 = buf[1];
  }

// MOTOR ONE
  // direction backward
  if(pot_1 < 128-10) {
    digitalWrite(7,0); // motor direction
    int mot_1 = map(pot_1, 0, 128-10, 255, 0);
    analogWrite(3,mot_1);
  }
  // stop motor in middle position
  else if((pot_1 > 128-9) && (pot_1 < 128+9)) {
    digitalWrite(7,0); // motor direction
    analogWrite(3,0); // motor speed
  }
  // direction forward
  else if(pot_1 > 128+10) {
    digitalWrite(7,1); // motor direction
    int mot_1 = map(pot_1, 128+10, 255, 255, 0);
    analogWrite(3,mot_1);
  }

// MOTOR TWO
  // direction backward
  if(pot_2 < 128-10) {
    digitalWrite(8,0); // motor direction
    int mot_2 = map(pot_2, 0, 128-10, 255, 0);
    analogWrite(11,mot_2);
  }
  // stop motor in middle position
  else if((pot_2 > 128-9) && (pot_2 < 128+9)) {
    digitalWrite(8,0); // motor direction
    analogWrite(11,0); // motor speed
  }
  // direction forward
  else if(pot_2 > 128+10) {
    digitalWrite(8,1); // motor direction
    int mot_2 = map(pot_2, 128+10, 255, 255, 0);
    analogWrite(11,mot_2);
  }
}

 

Python APRS Terminal

A simple Python GUI program to view APRS data from radio via serial port or Bluetooth. Easy to send messages and update status. Serial port and Bluetooth settings is available in settings panel. Autoscrolling feature is included to program so latest messages are always visible.

Features

  • Graphical user interface for those who not like text only mode.
  • Decodes KISS frame
  • Decodes ax.25 U frame
  • Decodes MIC-E format
  • Decodes APRS symbol and displays it on screen
  • Decodes APRS compressed data formats
  • Removes old items from the map
  • Removes traces of moving objects on the map

Supported and tested Hardware

  • Supports virtually any TNC which supports real or virtual serial port and KISS protocol.
  • Tested with Mobilinkd TNC2 through Bluetooth (with virtual serial port).

How does it work

Program decodes KISS frame and then ax.25 frame and lastly MIC-E data if available. Result is printed on window with colorful notation.

HELP – What to do if it does not work somehow?

Program should work on Python version 2.7.xx and onward. Run it with command “sudo python APRSterminal.py” without quotes. Contact me. I’m willing to help on any questions and can even add some features to program if necessary.

Source code is available from GitHub.

UART-controlled 7-segment display with Python GUI

This project uses this 7-segment display. It is based on Arduino. Display shows current gasoline price in Tampere, Finland. Gasoline price is middle price for 95E10 and it’s downloaded from www.polttoaine.net with Python code.

GUI (graphical user interface) is made with wxPython and wxFormBuilder. Update interval and serial port is user selectable.

Python code:

#! /usr/bin/python
# -*- coding: utf-8 -*-

import wx, wx.xrc
import threading
import serial
import urllib
from lxml.html import fromstring

class MyApp(wx.App):

    delay = 0
    runTime = 0

    def OnInit(self):
        self.res = wx.xrc.XmlResource("gui.xrc")
        self.frame = self.res.LoadFrame(None, "MyFrame1")
        self.text1 = wx.xrc.XRCCTRL(self.frame, "m_textCtrl1")
        self.text2 = wx.xrc.XRCCTRL(self.frame, "m_textCtrl2")
        self.slider1 = wx.xrc.XRCCTRL(self.frame, "m_slider1")
        self.frame.Bind(wx.EVT_BUTTON, self.on_evt_button, id=wx.xrc.XRCID("m_button1"))

        self.SetTopWindow(self.frame)
        self.frame.Show()
        
        self.delay = self.slider1.GetValue()*60
        self.updatePrice()
        self.thread = threading.Thread(target=self.delayTimer)
        self.thread.daemon = True
        self.thread.start()
        return True

    def on_evt_button(self, evt):
        self.delay = self.slider1.GetValue()*60

    def updatePrice(self):
        fopen = urllib.urlopen("https://www.polttoaine.net/Tampere")
        content = fopen.read()
        doc = fromstring(content)
        price = doc.find_class("Hinnat")[3].text_content()
        self.text1.SetValue(price)

        try:
            with serial.Serial(self.text2.GetValue(), 9600, timeout=1) as ser:
                time.sleep(5)
                ser.write(price+"\r")
        except IOError:
            print("Port cannot be opened")

    def delayTimer(self):
        self.runTime += 1
        if self.runTime >= self.delay:
            self.runTime = 0
            self.updatePrice()
        self.thread = threading.Timer(1,self.delayTimer)
        self.thread.daemon = True
        self.thread.start()       

app = MyApp(False)
app.MainLoop()

XRC layout file generated from wxFormBuilder:

<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<resource xmlns="http://www.wxwindows.org/wxxrc" version="2.3.0.1">
  <object class="wxFrame" name="MyFrame1">
    <style>wxDEFAULT_FRAME_STYLE|wxTAB_TRAVERSAL</style>
    <size>380,165</size>
    <title>95E10 Gasoline Price Display</title>
    <centered>1</centered>
    <aui_managed>0</aui_managed>
    <object class="wxFlexGridSizer">
      <rows>3</rows>
      <cols>3</cols>
      <minsize>380,165</minsize>
      <vgap>0</vgap>
      <hgap>0</hgap>
      <growablecols></growablecols>
      <growablerows></growablerows>
      <object class="sizeritem">
        <option>0</option>
        <flag>wxALL|wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL</flag>
        <border>5</border>
        <object class="wxStaticText" name="m_staticText1">
          <label>Current output</label>
          <wrap>-1</wrap>
        </object>
      </object>
      <object class="sizeritem">
        <option>0</option>
        <flag>wxALL|wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL</flag>
        <border>5</border>
        <object class="wxTextCtrl" name="m_textCtrl1">
          <style>wxTE_READONLY</style>
          <value></value>
        </object>
      </object>
      <object class="spacer">
        <option>1</option>
        <flag>wxEXPAND</flag>
        <border>5</border>
        <size>0,0</size>
      </object>
      <object class="sizeritem">
        <option>0</option>
        <flag>wxALL|wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL</flag>
        <border>5</border>
        <object class="wxStaticText" name="m_staticText2">
          <label>Update interval (minutes)</label>
          <wrap>-1</wrap>
        </object>
      </object>
      <object class="sizeritem">
        <option>0</option>
        <flag>wxALL|wxALIGN_BOTTOM|wxALIGN_CENTER_HORIZONTAL</flag>
        <border>5</border>
        <object class="wxSlider" name="m_slider1">
          <style>wxSL_HORIZONTAL|wxSL_LABELS</style>
          <size>100,-1</size>
          <value>30</value>
          <min>10</min>
          <max>120</max>
        </object>
      </object>
      <object class="sizeritem">
        <option>0</option>
        <flag>wxALL|wxALIGN_CENTER_VERTICAL</flag>
        <border>5</border>
        <object class="wxButton" name="m_button1">
          <label>Save settings</label>
          <default>0</default>
        </object>
      </object>
      <object class="sizeritem">
        <option>0</option>
        <flag>wxALL|wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL</flag>
        <border>5</border>
        <object class="wxStaticText" name="m_staticText3">
          <label>Serial port</label>
          <wrap>-1</wrap>
        </object>
      </object>
      <object class="sizeritem">
        <option>0</option>
        <flag>wxALL|wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL</flag>
        <border>5</border>
        <object class="wxTextCtrl" name="m_textCtrl2">
          <value>/dev/ttyUSB0</value>
        </object>
      </object>
    </object>
  </object>
</resource>

 

Reading UART with Python GUI application

Finally, I can say that I can program in the Python language. I learned Python from this tutorial. It is a good combination of the languages that I previously knew (PHP, Javascript and SQL). Structure also reminds me of Delphi and Basic languages.

Here is a program that reads 1-wire temperature sensor DS18B20 and displays it’s value on GUI (graphical user interface). GUI is made with wxPython library and with wxFormBuilder software.

In between sensor and computer there is Arduino Nano which transforms data from 1-wire to UART.

Python code:

#! /usr/bin/python
# -*- coding: utf-8 -*-

import wx
import wx.xrc
import serial
from thread import start_new_thread

class MyApp(wx.App):

    def OnInit(self):
        self.res = wx.xrc.XmlResource("gui.xrc")
        self.frame = self.res.LoadFrame(None, "MyFrame1")
        self.text1 = wx.xrc.XRCCTRL(self.frame, "m_textCtrl1")
        self.text2 = wx.xrc.XRCCTRL(self.frame, "m_textCtrl2")
        self.button1 = wx.xrc.XRCCTRL(self.frame, "m_button1")
        self.frame.Bind(wx.EVT_BUTTON, self.on_evt_button, id=wx.xrc.XRCID("m_button1"))
        
        self.SetTopWindow(self.frame)
        self.frame.Show()
        return True

    def on_evt_button(self, evt):
        self.button1.SetLabel("Wait...")
        self.button1.Enable(False)
        start_new_thread(readSerial,(self,))

def readSerial(self):
    try:
        with serial.Serial(self.text2.GetValue(), 9600, timeout=1) as ser:
            line = ""
            while len(line) == 0:
                ser.write("\n")
                line = ser.readline()
            self.text1.SetValue(line.splitlines()[0] + u"°C")
    except IOError:
        print("Port cannot be opened")
    self.button1.Enable(True)
    self.button1.SetLabel("Read sensor")

app = MyApp(False)
app.MainLoop()

XRC layout file generated from wxFormBuilder:

<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<resource xmlns="http://www.wxwindows.org/wxxrc" version="2.3.0.1">
  <object class="wxFrame" name="MyFrame1">
    <style>wxDEFAULT_FRAME_STYLE|wxTAB_TRAVERSAL</style>
    <size>-1,100</size>
    <title>1-Wire Temperature</title>
    <centered>1</centered>
    <aui_managed>0</aui_managed>
    <object class="wxGridSizer">
      <minsize>-1,100</minsize>
      <rows>2</rows>
      <cols>3</cols>
      <vgap>0</vgap>
      <hgap>0</hgap>
      <object class="sizeritem">
        <option>0</option>
        <flag>wxALL|wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT</flag>
        <border>5</border>
        <object class="wxStaticText" name="m_staticText1">
          <label>Outdoor temp</label>
          <wrap>-1</wrap>
        </object>
      </object>
      <object class="sizeritem">
        <option>0</option>
        <flag>wxALL|wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL</flag>
        <border>5</border>
        <object class="wxTextCtrl" name="m_textCtrl1">
          <style>wxTE_READONLY</style>
          <size>100,-1</size>
          <value>---</value>
        </object>
      </object>
      <object class="sizeritem">
        <option>0</option>
        <flag>wxALL|wxALIGN_CENTER_VERTICAL</flag>
        <border>5</border>
        <object class="wxButton" name="m_button1">
          <label>Read sensor</label>
          <default>0</default>
        </object>
      </object>
      <object class="sizeritem">
        <option>0</option>
        <flag>wxALL|wxALIGN_RIGHT|wxALIGN_CENTER_VERTICAL</flag>
        <border>5</border>
        <object class="wxStaticText" name="m_staticText2">
          <label>Port addr</label>
          <wrap>-1</wrap>
        </object>
      </object>
      <object class="sizeritem">
        <option>0</option>
        <flag>wxALL|wxALIGN_CENTER_VERTICAL|wxALIGN_CENTER_HORIZONTAL</flag>
        <border>5</border>
        <object class="wxTextCtrl" name="m_textCtrl2">
          <size>100,-1</size>
          <value>/dev/ttyUSB0</value>
        </object>
      </object>
    </object>
  </object>
</resource>

Arduino code:

#include <OneWire.h>
#include <DallasTemperature.h>

// Data wire is plugged into port 2 on the Arduino
#define ONE_WIRE_BUS 2

// Setup a oneWire instance to communicate with any OneWire devices
OneWire oneWire(ONE_WIRE_BUS);

// Pass our oneWire reference to Dallas Temperature. 
DallasTemperature sensors(&oneWire);

void setup(void) {
  Serial.begin(9600); // start serial port
  sensors.begin(); // Start up the library
}

void loop(void) { 
  while (Serial.available() > 0) { // if there's any serial available, read it
    if (Serial.read() == '\n') { // look for the newline
      sensors.requestTemperatures(); // Send the command to get temperatures
      Serial.println(sensors.getTempCByIndex(0)); // get temperature from first sensor only
    }
  }
}

 

Dual DC motor controller with Arduino

Two DC motor is independently controlled with Arduino Nano (Micro compatible)  and with a dual H-bridge using PWM. Speed and direction is selectable with two potentiometers. Each potentiometer value is read with Arduino analog input. When potentiometer is on it’s center position then motor is off state.

Video: https://youtu.be/dS4pnsJSH2k

Arduino Nano and Micro contains three timers. Timer0 is used with Arduino internal commands so it leaves two timer (timer1 and timer2) to use with custom programs. Timer1 works with pins 9, 10 and timer2 works with pins 11 and 3.

Pulse width is selected with command analogWrite(pin, value). Analog input is read with command analogRead(pin). Code is below.

void setup() {
  pinMode(7, OUTPUT);
  pinMode(8, OUTPUT);
}

void loop() {
  
  int pot_1 = analogRead(1); // potentiometers values
  int pot_2 = analogRead(0);

// MOTOR ONE
  // direction backward
  if(pot_1 < 512-20) {
    digitalWrite(7,0); // motor direction
    int mot_1 = map(pot_1, 0, 512-20, 255, 0);
    analogWrite(10,mot_1);
  }
  // stop motor in middle position
  else if((pot_1 > 512-19) && (pot_1 < 512+19)) {
    digitalWrite(7,0); // motor direction
    analogWrite(10,0); // motor speed
  }
  // direction forward
  else if(pot_1 > 512+20) {
    digitalWrite(7,1); // motor direction
    int mot_1 = map(pot_1, 512+20, 1023, 255, 0);
    analogWrite(10,mot_1);
  }

// MOTOR TWO
  // direction backward
  if(pot_2 < 512-20) {
    digitalWrite(8,0); // motor direction
    int mot_2 = map(pot_2, 0, 512-20, 255, 0);
    analogWrite(11,mot_2);
  }
  // stop motor in middle position
  else if((pot_2 > 512-19) && (pot_2 < 512+19)) {
    digitalWrite(8,0); // motor direction
    analogWrite(11,0); // motor speed
  }
  // direction forward
  else if(pot_2 > 512+20) {
    digitalWrite(8,1); // motor direction
    int mot_2 = map(pot_2, 512+20, 1023, 255, 0);
    analogWrite(11,mot_2);
  }
}