Showing posts with label serial communication. Show all posts
Showing posts with label serial communication. Show all posts

Sunday, March 7, 2021

Biggest WiFiChron display so far

In the world of 16-segment displays, Klais-16 is the biggest I have seen, at about 8cm (3") character height. Multiple displays can be daisy-chained and controlled through serial communication (1 pin, TX). It is open source and available to buy on Tindie at the very reasonable price of US$15 a piece.

The biggest challenge was mechanical, particularly, finding a way to mount the 8 individual displays. Let me explain. I received the displays as ready-to-use (assembled, programmed and tested) products. I did  not expected to dis-assemble them (not even partially) in order to mount them. The two mounting methods described in the documentation (using 15mm and 20mm profiles, respectively) ask for just that, basically to cut the original plastic rivets and, eventually, to replace them with (your own) M3 screws when fixing them to the rails. (I actually started going along with either described method until I realized I did not want to open them up). The easiest solution I found in the end was to use 1/2" x 1/2" L-profile, as shown below. For this, I only cut the middle rivets and used the holes to screw each individual display to the rails.


The software support consists in adding one class, DisplaySoftSerial.cpp, shown below.

#include <Arduino.h>
#include <SoftwareSerial.h>
#include "DisplaySoftSerial.h"

SoftwareSerial ss(7, 2);	// RX not used; TX=2;

//******************************************************************
//
void Display_t::setup()
{
  ss.begin(19200);
}

//******************************************************************
// draw the 8 characters on the screen;
//
void Display_t::writeDisplay(char* displayBuffer)
{
  char reverseBuffer[9] = {0};

  for (byte i=0; i<9; i++)
  {
    reverseBuffer[i] = (displayBuffer[7-i]);
  }
  ss.write(reverseBuffer);
}

//*******************************************************************
// brightness level is number between 0 and 7, 0 being the brightest;
//
void Display_t::setBrightness(uint8_t brightness)
{
  // cannot do;
}

//*******************************************************************
//
void Display_t::reset()
{
}


The Klais-16 display's baud rate can be selected in the range 4800 to 115200, through solder jumpers. The default (no soldering) is 115200. I set it to 19200 because:
  • SoftwareSerial library cannot handle 115200
  • only one solder bridge is required
As you can see from the code above, the brightness cannot be adjusted (or at least I am not aware). Maybe future versions will implement this feature.

The WiFiChron with the 8 Klais-16 displays consumes an average of 700mA (at 5V). The brightness is not amazing, probably because some of the light is absorbed by the top translucent PCB.
I think it can make a great clock for interiors (schools, hallways etc.), visible from at least 10 meters away.


Thursday, April 19, 2012

Wise Clock 4 with remote "Alarm stop" button

The Ramos project brought the novel idea of a wireless alarm-stop button: instead of just reaching out to  press on the top of your nightstand alarm clock, you now have to actually get out of the bed and walk to a remote corner of your dwelling to click on a keypad. No chance you will return to sleep afterward :)

This remote alarm-stop feature for Wise Clock 4 can be implemented in several different ways, all of them using of the on-board XBee socket:
  • through a Bluetooth module;
  • through WiFi, using the Roving Networks WiFly module;
  • through XBee radios.
The cheapest and easiest would be the Bluetooth solution, providing that you already have a BT device (e.g. Android phone/tablet) to communicate with. In pseudo-code, it should look like this:

if (alarm is ON)
{
    while (Serial1.available())
    {
       read characters received;
       if (it is the expected string)
       {
            set alarm OFF;
        }
    }
}

The WiFi solution would require a second WiFi device (phone/tablet) to access a web site, or maybe they could talk directly (sockets), using their IP addresses.

The solution based on XBee relies on direct communication between two XBee radios. One would have to build the remote device, probably around an Arduino, with a keypad and an XBee.

Updated Feb 26, 2018: A good XBee remote control would be the SparkFun Wireless joystick.
Another, much cheaper solution, that does not requires software changes, would use the M4 backpack, as detailed here.

Saturday, June 27, 2009

Storing strings in EEPROM, byte by careful byte

By popular demand (meaning somebody asked :), in this article I will show how I store ASCII characters(1) in an I2C EEPROM, and then how I fetch them for display purpose.

(1) In this article, “character” and “byte” have the same meaning, since one ASCII character can be stored in a byte.

I used this solution in Wise Clock, where 32KB (aprox 16 book pages) of text(2) is displayed line by line, as a sequence of quotations.
(2) "Text" is a sequence of ASCII characters.
Obviously, before the text is retrieved from EEPROM, the text must be written, once, to EEPROM. So, the very first step is to load the text into the EEPROM. To do this, I use two pieces of software:
  • one sketch (running on Arduino) that reads, from the serial port, byte by byte, and then writes it into the EEPROM;

void loop()
{
if (isWrite)
{
if (Serial.available() > 0)
{
// read next character from COM port;
byte incomingByte = Serial.read();
if (isCommand && (incomingByte == 'r' || incomingByte == 'l'))
{
isWrite = false;
Serial.println("read eprom");
switch (incomingByte)
{
case 'r':
isReadLineByLine = false;
break;
case 'l':
isReadLineByLine = true;
break;
}
}
else
{
isCommand = false;
if (incomingByte == '\\')
{
// don't write the backslash;
isCommand = true;
}
else
{
// the following debug lines (serial.print...) take about 25 more ms than the write itself (10 ms);
// it is a good idea to comment them out if we transfer the content (32K) of a whole file;
/*
Serial.print("write to addr ");
Serial.print(crtWriteAddress);
Serial.print(" byte ");
Serial.println(incomingByte, BYTE);
*/
writeNextByte(incomingByte);
}
}
}
}

  • one application (running on PC and already presented here) that reads the characters from a text file and sends them to serial port, byte by byte.
Once in the EEPROM, the text can be retrieved when required. In Wise Clock, I chose to read the text from the EEPROM one line at a time. Since each line ends with a CR (“carriage return”), ASCII character 13, to get a line of text I read characters until a CR is found


else // reading the eeprom;
{
if (isReadLineByLine)
{
// read a whole line into the message buffer;
lastReadByte = readNextByte();
while (lastReadByte != 13 && lastReadByte != 0xFF)
{
*msgBufferPtr++ = lastReadByte;
lastReadByte = readNextByte();
}
*msgBufferPtr++ = 0;
Serial.println(msgBuffer);
msgBufferPtr = &msgBuffer[0]; // reset buffer pointer;
delay(50);
}
else
{
if (0xFF != lastReadByte)
{
Serial.print("read from addr ");
Serial.print(crtReadAddress);
Serial.print(" byte ");
lastReadByte = readNextByte();
Serial.print(lastReadByte, BYTE);
Serial.print(" (");
Serial.print(lastReadByte, DEC);
Serial.println(")");
delay(50);
}
}
}
}
If you want to cut an paste this code, you may need the following lines as well:
// I2C Bus address of 24LC256 EEPROM;
// if more than one eeprom, they will have different addresses (h/w configured);
#define I2C_ID 0x50
// global address of the last written byte;
unsigned int crtWriteAddress = 0;
// global address of the last read byte;
unsigned int crtReadAddress = 0;
boolean isWrite = true;
byte lastReadByte = 0;
// current line string is built by copying each character to the position of the pointer, then advancing the pointer;
char msgBuffer[200] = {0};
char* msgBufferPtr = &msgBuffer[0];
boolean isCommand = false;
boolean isReadLineByLine = false;
void writeByte(int i2cId, unsigned int eeaddress, byte data)
{
int rdata = data;
Wire.beginTransmission(i2cId);
Wire.send((int)(eeaddress >> 8)); // Address High Byte
Wire.send((int)(eeaddress & 0xFF)); // Address Low Byte
Wire.send(rdata);
Wire.endTransmission();
delay(10); // NEED THIS DELAY!
}
void writeNextByte(byte data)
{
writeByte(I2C_ID, crtWriteAddress, data);
crtWriteAddress++;
}
byte readByte(int i2cId, unsigned int eeaddress)
{
byte rdata = 0xFF;
Wire.beginTransmission(i2cId);
Wire.send((int)(eeaddress >> 8)); // Address High Byte
Wire.send((int)(eeaddress & 0xFF)); // Address Low Byte
Wire.endTransmission();
Wire.requestFrom(i2cId, 1);
if (Wire.available()) rdata = Wire.receive();
return rdata;
}
byte readNextByte()
{
byte rdata = readByte(I2C_ID, crtReadAddress);
crtReadAddress++;
return rdata;
}
(The last 4 I2C functions are copied and adapted from playground.)

Thursday, June 4, 2009

Lessons learned 2

This is how the board looks after it was populated.


The main reason for the hack was to install a connector to Tx/Rx pins of the microcontroller. So, always wire Rx/Tx pins of the microcontroller to an external connector (e.g. FTDI) that you can use for serial communication. Otherwise, your software issue (sketch fix/upgrade) becomes hardware.

(to be continued)

Friday, May 22, 2009

Arduino writes to file

One of the frequently asked question on the Arduino forum is "how can Arduino save data to a file?" (see this thread for example). The quick answer is this: Arduino sends data over the serial port to the PC, which, in turn, is running an executable that reads the serial port. Every byte of data (or ASCII character) received is then saved to a file. So, this mechanism has two parts:
1. a piece of the sketch, running on Arduino, will send data using the function Serial.print();
2. a piece of code (executable, script etc) running on PC, will receive data from the serial port.

An example of VBscript that does just that is shown below. The characters read from COM6 (baud rate 9600, 8 bits, no parity, 1 stop bit) are saved to file "C:\results.txt".

Const ForReading = 1
Const ForWriting = 2

Set fso = CreateObject("Scripting.FileSystemObject")
Set com = fso.OpenTextFile("COM6:9600,N,8,1", ForReading)

Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile("C:\results.txt", ForWriting, True)

MsgBox("Start to read data from COM")

Do While com.AtEndOfStream <> True
s = com.ReadLine
objFile.WriteLine(s)
WScript.Sleep(200)
Loop

objFile.Close
com.Close()

A similar approach can be used for reading characters from a file.

The following is an example VBscript that reads the characters from a text file ("C:\docs\quotes.txt") and sends them on port COM7(9600,N,8,1) to Arduino. I use it to load text messages (quotations) to an external EEPROM (24LC256).


Const ForReading = 1
Const ForWriting = 2

'------------------------------------------------------------------------------------------------
' open USB serial port (COMx);
'
' If the serial monitor in Arduino IDE is open, you will get an "access denied" error.
' Just make sure that the serial monitor is closed (so bytes are not sent by the arduino board).
'------------------------------------------------------------------------------------------------

Set fso = CreateObject("Scripting.FileSystemObject")
Set com = fso.OpenTextFile("COM7:9600,N,8,1", ForWriting)


'---------------------------------------------
' read content of text file line by line;
' write line to COMx;
'---------------------------------------------

Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile("C:\docs\quotes.txt", ForReading)

MsgBox("Ready to write file content to COM")

Do While objFile.AtEndOfStream <> True
'---------------------------------------------------------------------------------------------------------
' read 10 characters at a time; arduino serial buffer cannot take more than 32 characters;
' writing a character to eeprom takes about 11 ms (assuming that there is no serial.prints in the loop);
' therefore, after each batch of 10 chars sent to COM, we should wait no less than 110 ms;
' we use 200 to have a margin of safety;
'---------------------------------------------------------------------------------------------------------
strChars = objFile.Read(10)
com.Write(strChars)
WScript.Sleep(200)
Loop

objFile.Close
com.Close()

MsgBox("Finished writing to COM")
In order to use the above scripts, copy the code, paste it into a text file and name it with the extension "vbs". Just by double clicking on the file, Windows will invoke wscript.exe, which will then execute the VBscript code.
NB: The above code was formatted by http://formatmysourcecode.blogspot.com/

Sunday, April 19, 2009

Serial communication with Arduino clone

Today I had problems connecting on serial with an Arduino clone, through the FTDI Basic (3.3V) board from sparkfun. I powered the board from 4 AA batteries (aprox 6V). Serial communication was just not happening, Tx and Rx LEDs were off. I checked the FTDI board, the Atmega board, the serial port, the communication parameters, they all seemed ok. I finally figured out the culprit: the 6V power source! I replaced it with a lower voltage, around 5V, and the serial communication worked like a charm, as before.

The setup looks like in this photo.



Although, the Arduino works just fine with the 4 AA batteries (after the sketch is loaded), it seems that the serial communication is affected, that is, it does not work in this case. Good to know, in case anyone runs into the same kind of situation.