How to solve Bad File Descriptor for Linux and Modbus - linux

I am trying to setup a Half Duplex RS-485 communication using libmodbus on a Raspberry Pi running Raspian Buster, with a FTDI USB to Serial adapter. My FTDI adapter shows as ttyUSB0 when I run ls /dev/.
I tried the following sample code:
#include <modbus.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
int main(void) {
modbus_t *ctx = modbus_new_rtu("/dev/ttyUSB0", 19200, 'N', 8, 1);
if (ctx == NULL) {
fprintf(stderr, "Unable to create the libmodbus context\n");
return 0;
}
if (modbus_rtu_set_serial_mode(ctx, MODBUS_RTU_RS485) == -1) {
fprintf(stderr, "Error setting the serial port as RS-485\n");
fprintf(stderr, "errno: %d\n (EBADF == 9)", errno);
modbus_free(ctx);
return 0;
}
}
Compiled with gcc test1.c -I/usr/include/modbus -lmodbus.
And I get errno as 9, or EBADF, even if I run this code with sudo.

There is a very easy solution to your problem: just don't set MODBUS_RTU_RS485, quite likely you don't need it.
This mode is actually a workaround for devices without automatic (hardware) direction control. As you know, Modbus RTU works over a half-duplex RS485 link (only one device is allowed to talk while all others must be listening only), and hence requires an additional (to RX and TX) signal to control what device is writing to the bus at all times (direction control).
So you would only need to set MODBUS_RTU_RS485 if your device lacks this feature, which is nowadays quite unlikely or if you are building your own transceiver. Most devices based on the FTDI chip, in particular, should have this feature since the chip itself has a TXDEN (transmit enable) pin. See here for more details and a trick to expose the TXDEN signal to a non-default pin.
It is when you don't have this feature (one frequent scenario is when you want to use the embedded UART on your Rpi for Modbus over RS485, implementing your own transceiver) that you need a software (or hardware) workaround. And that's where MODBUS_RTU_RS485 should come handy, repurposing the RTS flow control signal. Unfortunately, most serial drivers (including ftdi_sio, the one you are probably using) don't support this mode (refer again to the link above).
Luckily, there are workarounds to the workaround: see here for a complete discussion. You can also take a look at this answer where I explained how to set up libmodbus with support for toggling the direction on the bus using a GPIO pin on a Rpi (also applicable to most SBCs, I've used this method successfully with a Pocket Chip computer, for instance).
You can find more background on this issue elsewhere: here and here.

Related

Capturing power-off interrupt for i.MX6UL (linux kernel)

Context
I'm using an i.MX6 (IMXULL) application processor, and want to know in software when the power-off button has been pressed:
Luckily, the IMX6ULL reference manual explains that this should be possible:
Section 10.5: ONOFF Button
The chip supports the use of a button input signal to request main SoC power state changes (i.e. On or Off) from the PMU. The ONOFF logic inside of SNVS_LP allows for connecting directly to a PMIC or other voltage regulator device. The logic takes a button input signal and then outputs a pmic_en_b and set_pwr_off_irq signal. [...] The logic has two different modes of operation (Dumb and Smart mode).
The Dumb PMIC Mode uses pmic_en_b to issue a level signal for on and off. Dumb pmic mode has many different configuration options which include (debounce, off to on time, and max time out).
(Also available in condensed form here on page 18)
Attempt
Therefore, I have built a trivially simple kernel module to try and capture this interrupt:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/syscalls.h>
#include <linux/interrupt.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("John Doe <j.doe#acme.inc>");
// Forward declaration
irqreturn_t irq_handler (int, void *);
// Number of interrupt to capture
#define INTERRUPT_NO 36
static int __init pwr_ctl_init (void)
{
pr_err("init()\n");
return request_irq(INTERRUPT_NO, irq_handler, IRQF_SHARED, "onoff-button",
(void *)irq_handler);
}
static void __exit pwr_ctl_exit (void)
{
pr_err("exit()\n");
free_irq(INTERRUPT_NO, NULL);
}
irqreturn_t irq_handler (int irq, void *dev_irq)
{
pr_err("interrupt!\n");
return IRQ_HANDLED;
}
module_init(pwr_ctl_init);
module_exit(pwr_ctl_exit);
Problem
However, I cannot find any information about what the number of the interrupt is. When searching on the internet, all I get is this one NXP forum post:
ONOFF button doesn't interrupt
Which hints it should be 36. However, I have found that this isn't the case on my platform. When I check /proc/interrupts 36 is already occupied by 20b4000.ethernet. Because the application manual also mentions that it is generated by the SNVS low power system, I checked the device-tree and found the following information:
snvs_poweroff: snvs-poweroff {
compatible = "syscon-poweroff";
regmap = <&snvs>;
offset = <0x38>;
value = <0x60>;
mask = <0x60>;
status = "disabled";
};
snvs_pwrkey: snvs-powerkey {
compatible = "fsl,sec-v4.0-pwrkey";
regmap = <&snvs>;
interrupts = <GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>;
linux,keycode = <KEY_POWER>;
wakeup-source;
status = "disabled";
};
This information seems useful for knowing that SNVS is the interrupt controller, but not how to capture this set_pwr_off_irq signal.
Conclusion
How do I capture the ON/OFF interrupt supposedly generated by SNVS?
How do I determine the number of an interrupt from the device-tree (if applicable at all)
Am I misunderstanding something about how the ONOFF feature works? Is it possible to capture this from a kernel module at all?
Edit
This edit answers some user questions, and then goes into new information about the problem I have since discovered:
User Questions
Processor: The processor is an NXP i.MX 6UltraLite / 6ULL / 6ULZ ARM Cortex A7.
New Information
SNVS Driver: Using my build system kernel configuration, I have modified and verified that the snvs_pwrkey driver (see here) is enabled. My modification consists of adding a single kprint statement to the interrupt routine to see if the button trips it. This did not work
I have tried updating the driver to a newer version, which claims to support newer i.MX6 processors. This also did not work
I have tried to load the driver as a kernel module for easier debugging. This is not possible, as the kernel configuration requires this be enabled and I cannot remove it from being statically built into the kernel.
The answer is rather anticlimactic. In short, there was a device-tree overlay that was disabling my changes to snvs_pwrkey, even when I had enabled it. Once I located and removed the overlay, the driver (snvs_pwrkey.c) was working as expected.
As for the IRQ number, it turns out that the IRQ for the power button is 45 as interpreted through Linux. The interrupt is not configured for sharing, so my kernel module could not be loaded.
If you want to capture power button toggle events, I suggest modifying the driver to add some output, and then perhaps adding a udev rule to capture button presses. I will update my answer with an example ASAP.

Need to find and open a USB serial device on linux

I'm writing a small C application to run on my (Linux) QNAP NAS that will talk to an Arduino (I have no difficulty with any of the USB code for the arduino). (The arduino has a trusted application on it that accepts text commands via USB serial.)
My wish was to find it using the USB vendor/product IDs (only half implemented at the moment). What I have so far (see below) works quite nicely in that it does find the device.
// runs on NAS
#include <stdio.h>
#include <usb.h>
main () {
struct usb_bus *bus;
struct usb_device *dev;
usb_init();
usb_find_busses();
usb_find_devices();
for (bus = usb_busses; bus; bus = bus->next) {
for (dev = bus->devices; dev; dev = dev->next) {
printf("Trying %s/%s\n", bus->dirname, dev->filename);
printf("\tVendor = 0x%04x\n", dev->descriptor.idVendor);
printf("\tBus = 0x%03x\n", bus->location);
printf("\tFile = %s\n", dev->filename);
if (dev->descriptor.idVendor==0x403) {
printf("\t HEY, THIS IS MINE!\n");
usb_dev_handle *handle = usb_open(dev);
printf("\t HANDLE 0x%08x\n", (int) handle);
//printf(handle, "1,5,62,75\n");
usb_close(handle);
}
}
}
}
The trouble is that now I want to send/receive a little bit of text with the device and I don't know how to do that.
I have expected I should be generating a device name from something in the usb_device struct and then open it like a file (like one would do on Windows).
If that's correct, I need to know the correct way to find out the device name...
I believe I'm using libusb.
I happen to know that -- as currently configured/connected -- it's ttyUSB0 but I'd like to know that using code.
thank you!
I suggest using libusbp. It is a C library with a C++ wrapper, and there is example code showing how to get the name of a serial port based on the vendor ID and product ID of the USB device:
https://github.com/pololu/libusbp/blob/master/examples/port_name/port_name.cpp
libusb is a library used for low level communication with USB devices. But when your communication is limited just to reading device's USB descriptor - libusb is not the best tool.
Linux systems use udev subsystem to manage hot-plug devices. udev reads descriptors of all plugged USB devices and stores them in its database. libudev is the library that you should use to get such info like device names of enumerated devices. In your case you need to remember that usb_device and usb_interface are separate things - only the latter would have the tty device file assigned to it.
Alternatively you could just use udev config to assign a constant device name to your specific device. So you would not have to be looking for it.

Characters not properly displayed in serial monitor in arduino

Can anyone tell me why the characters are not getting printed properly in the serial monitor of Arduino? I am pasting the arduino code.
#include <SoftwareSerial.h>
#include <LiquidCrystal.h>
// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(12,11,5,4,3,2);
int bluetoothTx = 15;
int bluetoothRx = 14;
SoftwareSerial bluetooth(bluetoothTx, bluetoothRx);
int incomingByte;
void setup() {
pinMode(53, OUTPUT);
Serial.begin(9600);
lcd.begin(16, 2);
lcd.clear();
bluetooth.begin(115200); // The Bluetooth Mate defaults to 115200bps
delay(320); // IMPORTANT DELAY! (Minimum ~276ms)
bluetooth.print("$$$"); // Enter command mode
delay(15); // IMPORTANT DELAY! (Minimum ~10ms)
bluetooth.println("U,9600,N"); // Temporarily Change the baudrate to 9600, no parity
bluetooth.begin(9600); // Start bluetooth serial at 9600
lcd.print("done setup");
}
void loop()
{
lcd.clear();
Serial.print("in loop");
//Read from bluetooth and write to usb serial
if(bluetooth.available()) {
Serial.print("BT here");
char toSend = (char)bluetooth.read();
Serial.print(toSend);
lcd.print(toSend);
delay(3000);
}delay(3000);
}
Can anyone take a look into it. It does not print the character that I provide instead it prints something else like 'y' with 2 dots on top etc. Tried almost all the available solution.
Your issues could be one of a couple things. First and easiest to check is COMMON GROUND. Did you connect just the RX and TX pins or also the GND (ground) pin? Make sure that the ground from the BT mate is connected to the Arduino ground.
If you have done that, then your issue is with the baud rate. I'm pretty sure that SoftwareSerial can't read at baud rates beyond 57600. Arduino.cc docs say it can read at 115200, but other places say it will only write up to 115200.
To test this, you will either need to change the settings for this on the Bluetooth Mate or use a Mega or Leonardo which will have a hardware serial port (other than the one used for USB) which you should be able to configure for 115200.
If you try it with hardware serial either on a Mega or just using an FTDI or something and the messages still look garbled then perhaps the bluetooth mate is not actually configured to talk at 115200 as it claims. Trying reading the docs or testing with other baud rates.
Check whether error is present due to one of the following reasons:-
1) You haven't given any command to exit from the data mode. After setting the baudrate to 9600, you are directly switching to loop. You haven't given the command to exit the command mode.
2) I too had the same problem when I was using RN171 Wi-Fi module. The cause of the problem in my case was because I was sending data to Wi-Fi module in integer format instead of uint_8. While reading from the Wi-Fi module serially with arduino mega, I was reading it in the format of characters.
You have to remember that int is actually signed 16 bit integer. So while sending data to your Bluetooth module you have to send it as uint_8 or ASCII values of the characters that you want to send. You should also read it in the same format as you sent it.
3) If these are not the error then as calumb said, there can be error in setting the bluetooth module in command mode. You haven't checked for reply from bluetooth module whether it is really in command mode or not. You must read an CMD reply from bluetooth module and at the end of every command a reply of ack to conform that its really done what you want it to do.
This may be because of Bluetooth parsing data simultaneously. when sending two different data at the same time this may happens. try to control your data flow.

Can I use UART on MSP-EXP430F5529LP from Energia in order to communicate on pins p3.3 and p3.4 (rx and tx respectively)?

Can I use UART on MSP-EXP430F5529LP from Energia in order to communicate on pins p3.3 and p3.4 (rx and tx respectively)?
I already use UART in order to communicate with my PC via USB. To do so, I use Serial.println() and such. Now that one UART is taken, how do I configure and use second UART to go to these pins? Or would it be better to rewire my Bluetooth chip (BlueGiga wt32) to some other pins?
Configuration aside, Serial does not seem to allow for multiple UARTs. How does it know which UART to print to?
For some reason, I could not find any manual on interacting with wt32 from Energia, or on interacting with multiple UARTs on Energia.
Edit: found this link: http://forum.43oh.com/topic/3942-stellaris-lm4f120-multiple-uart-problem/
only, incidentally, they say it does not work. Still, a lead.. but it is my understanding that I still have to configure UART on those two pins, if at all possible.
Edit2 Found MultiSerial example in Energia:
/*
Multple serial test
Receives from the main serial port, sends to the others.
Receives from serial port 1, sends to the main serial (Serial 0).
The circuit:
* Any serial device attached to Serial port 1
* Serial monitor open on Serial port 0:
created 30 Dec. 2008
by Tom Igoe
This example code is in the public domain.
*/
void setup() {
// initialize both serial ports:
Serial.begin(9600);
Serial1.begin(9600);
}
void loop() {
// read from port 1, send to port 0:
if (Serial1.available()) {
int inByte = Serial1.read();
Serial.write(inByte);
}
}
Now, this may sound like a really noob question, but where is my Serial1?? Is it somewhere on my pinout? The corresponding page in Energia manual is just a stub.
Now, this link: http://energia.nu/Tutorial_SerialCallResponse.html has a pinout according to which P1.1 is TXD, P1.2 is RXSD, which is something that I have not seen documented elsewhere. I suspect that it is assigned in this particular example, only I don't see the assignment in the code; also, I suspect that this is the backchannel, unless the switch is turned. Confused!
Edit3: found SoftwareSerial example that turns pins of your choice into a RX and TX. So at least I probably have a software solution. Of course, I'd prefer hardware. The manual for the launchboard says the hardware supports up to 4 serial ports, but how? Where are the pins?
Sorry I keep adding to this. I'll tidy it out when there is a solution.
If you dont need those uart pins to talk to the host you can remove the jumpers on that board that connect rxd and txd and then connect those to your bluetooth module.
Serial.begin(); works for backchannel UART pins(Txd,Rxd between target ic & debugger ic marked in white squares in below image)
http://energia.nu/wordpress/wp-content/uploads/2015/03/2016-06-09-LaunchPads-MSP432-2.0-%E2%80%94-Pins-Maps.jpg
Serial1.begin(); works for P3_2 & P3_3

UART initialisation: Prevent UART to pull RTS high

I'm writing a RS485 driver for an ARM AT91SAM9260 board on Linux.
When I initialise the UART, the RTS signal line gets high (1). I guess this would and should be the standard behaviour in RS232 operation mode. In RS485 mode however this is not wanted.
I'm using the standard functions provided by the arm-arch section to initialise the UART. Therefore the significant steps are:
at91_register_uart(AT91SAM9260_ID_US2, 3, ATMEL_UART_CTS | ATMEL_UART_RTS);
//consisting of:
// >> configure/mux the pins
at91_set_A_periph(AT91_PIN_PB10, 1); /* TXD */
at91_set_A_periph(AT91_PIN_PB11, 0); /* RXD */
if (pins & ATMEL_UART_RTS)
at91_set_B_periph(AT91_PIN_PC8, 0); /* RTS */
if (pins & ATMEL_UART_CTS)
at91_set_B_periph(AT91_PIN_PC10, 0); /* CTS */
// >> associate the clock
axm_clock_associate("usart3_clk", &pdev->dev, "usart");
// >> et voilĂ 
As you can see with
at91_set_B_periph(AT91_PIN_PC8, 0);
the pull-up on the RTS pin isn't activated.
Why does the UART set the RTS high?
Just because this would be the standard
behaviour in RS232 mode?
Wouldn't it be a better standard for
the UART to keep silent till the
operation mode is explicitly set?
A high RTS signal after initialisation seems to be the standard behaviour on many platforms. It manly depends which serial operation mode the start-up routines anticipates for the interface.
To prevent RTS-high on a ATMEL AT91SAM9260 board running Linux, you have to put the UART into the right mode BEFORE muxing at91_set_X_periph() the Pins and register the device.
Since Linux Kernel Version 2.6.35, the ATMEL serial driver supports the RS485 mode. In this driver the UART is properly configured before setting the Pins (GPIO) to there role.
For my embedded device which is running an older Linux version, I solved the problem with the following line of codes:
/* write control flags */
control |= ATMEL_US_RTSEN;
mode |= ATMEL_US_USMODE_RS485;
UART_PUT(uartbaseaddr, ATMEL_US_CR, control);
UART_PUT(uartbaseaddr, ATMEL_US_MR,mode);
Now the Pins can be muxed the their role
at91_set_X_periph(RTS_PIN, 0);

Resources